<?php

/**
 * PayComponent 支付组件
 *
 * @author lizaifang <lizf@wanthings>
 * @version 0.02
 * @changelog
 * 0.02
 * 0.01
 */
class PayComponent extends CComponent
{
    const PLATFORM_TYPE_ALIPAY = 100;// 支付宝
    const PLATFORM_TYPE_BOC = 200;// 中国银行
    const PLATFORM_TYPE_WECHAT = 300;// 微信支付
    const PLATFORM_TYPE_PAYPAL = 400;// 贝宝支付
    const PLATFORM_TYPE_UNIONPAY = 500;// 银联在线支付
    const PLATFORM_TYPE_JDPAY = 600;// 京东支付/原网银在线

    const PAYMENT_TYPE_ALIPAY = 1;// 支付宝SDK
    const PAYMENT_TYPE_ALIPAY_WAP = 2;// 支付宝WAP
    const PAYMENT_TYPE_WECHAT = 3;// 微信SDK
    const PAYMENT_TYPE_WECHATJS = 4;// 微信JSAPI
    const PAYMENT_TYPE_UNION = 5;// 银联支付
    const PAYMENT_TYPE_PAYECO = 6;// 易联支付
    const PAYMENT_TYPE_LLPAY = 7;// 连连支付
    const PAYMENT_TYPE_SWIFTPAY = 8;// swift支付
    const PAYMENT_TYPE_ALLINPAY = 9;// 通联支付

    const LOG_LEVEL_NONE = 1;
    const LOG_LEVEL_FILE = 2;
    const LOG_LEVEL_PAGE = 3;

    private $_paymentType = NULL;
    private $_errors = array();
    private $_merchantParams = array();

    private $_subject_prefix = '品牌-';

    /**
     * 调试标志
     *
     * @var bool
     */
    public $debug = false;
    public $log_level = self::LOG_LEVEL_FILE;

    // 微信
    private $appid = '';
    private $mch_id = '';
    private $key = '';
    private $appsecret = '';
    private $apiclient_cert = '';
    private $apiclient_key = '';

    // 银联
    public $mer_Id = '';
    public $cert_path = '';
    public $cert_pwd = '';
    public $enc_cert_path = '';
    public $enc_cert_pwd = '';
    public $union_publicKey = '-----BEGIN CERTIFICATE-----
MIIEOjCCAyKgAwIBAgIFEAJkAUkwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC
Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTUxMjA0MDMyNTIxWhcN
MTcxMjA0MDMyNTIxWjB5MQswCQYDVQQGEwJjbjEXMBUGA1UEChMOQ0ZDQSBURVNU
IE9DQTExEjAQBgNVBAsTCUNGQ0EgVEVTVDEUMBIGA1UECxMLRW50ZXJwcmlzZXMx
JzAlBgNVBAMUHjA0MUBaMTJAMDAwNDAwMDA6U0lHTkAwMDAwMDA2MjCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMUDYYCLYvv3c911zhRDrSWCedAYDJQe
fJUjZKI2avFtB2/bbSmKQd0NVvh+zXtehCYLxKOltO6DDTRHwH9xfhRY3CBMmcOv
d2xQQvMJcV9XwoqtCKqhzguoDxJfYeGuit7DpuRsDGI0+yKgc1RY28v1VtuXG845
fTP7PRtJrareQYlQXghMgHFAZ/vRdqlLpVoNma5C56cJk5bfr2ngDlXbUqPXLi1j
iXAFb/y4b8eGEIl1LmKp3aPMDPK7eshc7fLONEp1oQ5Jd1nE/GZj+lC345aNWmLs
l/09uAvo4Lu+pQsmGyfLbUGR51KbmHajF4Mrr6uSqiU21Ctr1uQGkccCAwEAAaOB
6TCB5jAfBgNVHSMEGDAWgBTPcJ1h6518Lrj3ywJA9wmd/jN0gDBIBgNVHSAEQTA/
MD0GCGCBHIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20u
Y24vdXMvdXMtMTQuaHRtMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6Ly91Y3JsLmNm
Y2EuY29tLmNuL1JTQS9jcmw0NDkxLmNybDALBgNVHQ8EBAMCA+gwHQYDVR0OBBYE
FAFmIOdt15XLqqz13uPbGQwtj4PAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqG
SIb3DQEBBQUAA4IBAQB8YuMQWDH/Ze+e+2pr/914cBt94FQpYqZOmrBIQ8kq7vVm
TTy94q9UL0pMMHDuFJV6Wxng4Me/cfVvWmjgLg/t7bdz0n6UNj4StJP17pkg68WG
zMlcjuI7/baxtDrD+O8dKpHoHezqhx7dfh1QWq8jnqd3DFzfkhEpuIt6QEaUqoWn
t5FxSUiykTfjnaNEEGcn3/n2LpwrQ+upes12/B778MQETOsVv4WX8oE1Qsv1XLRW
i0DQetTU2RXTrynv+l4kMy0h9b/Hdlbuh2s0QZqlUMXx2biy0GvpF2pR8f+OaLuT
AtaKdU4T2+jO44+vWNNN2VoAaw0xY6IZ3/A1GL0x
-----END CERTIFICATE-----';

    //易联支付生产公钥
    public $payeco_publicKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoymAVb04bvtIrJxczCT/DYYltVlRjBXEBFDYQpjCgSorM/4vnvVXGRb7cIaWpI5SYR6YKrWjvKTJTzD5merQM8hlbKDucxm0DwEj4JbAJvkmDRTUs/MZuYjBrw8wP7Lnr6D6uThqybENRsaJO4G8tv0WMQZ9WLUOknNv0xOzqFQIDAQAB
-----END PUBLIC KEY-----';

    //易联支付测试公钥
    public $payeco_test_publicKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRxin1FRmBtwYfwK6XKVVXP0FIcF4HZptHgHu+UuON3Jh6WPXc9fNLdsw5Hcmz3F5mYWYq1/WSRxislOl0U59cEPaef86PqBUW9SWxwdmYKB1MlAn5O9M1vgczBl/YqHvuRzfkIaPqSRew11bJWTjnpkcD0H+22kCGqxtYKmv7kwIDAQAB
-----END PUBLIC KEY-----';

    // 易联支付版本
    public $payeco_merchant_id = '';
    public $payeco_cert_path = '';
    public $payeco_cert_pwd = '';
    public $payeco_url = 'https://mobile.payeco.com';
    public $payeco_test_url = 'https://testmobile.payeco.com';

    // 易联支付web版本
    public $payeco_web_merchant_id = '';
    public $payeco_web_merchant_tid = '';
    public $payeco_web_merchant_key = '';
    public $payeco_web_url = 'https://ebank.payeco.com/services/DnaPayB2C';
    public $payeco_web_test_url = 'https://test.payeco.com:9443/DnaOnlineTest/servlet/DnaPayB2C';

    // 支付宝
    private $primaryKey = '';
    private $partner = '';
    private $sellId = '';
    private $alipay_appid = '';
    private $md5Key = '';
    private $pubKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRA
FljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQE
B/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5Ksi
NG9zpgmLCUYuLkxpLQIDAQAB
-----END PUBLIC KEY-----';

    private $llpay_merchant_id = '';
    private $llpay_private_key = '';
    private $llpay_merchant_key = '';

    // 威富通
    private $swiftpass_mch_id = '';
    private $swiftpass_key = '';

    // 通联
    private $allinpay_merchant_id = '';
    private $allinpay_md5_key = '';

    public function __construct($merchantParams, $debug = true)
    {
        $this->debug = $debug;
        foreach ($merchantParams as $key => $value) {
            $this->{$key} = $value;
        }

        if ($this->debug) {
            $this->union_publicKey = '-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIFEDRVM3AwDQYJKoZIhvcNAQEFBQAwITELMAkGA1UEBhMC
Q04xEjAQBgNVBAoTCUNGQ0EgT0NBMTAeFw0xNTEwMjcwOTA2MjlaFw0yMDEwMjIw
OTU4MjJaMIGWMQswCQYDVQQGEwJjbjESMBAGA1UEChMJQ0ZDQSBPQ0ExMRYwFAYD
VQQLEw1Mb2NhbCBSQSBPQ0ExMRQwEgYDVQQLEwtFbnRlcnByaXNlczFFMEMGA1UE
Aww8MDQxQDgzMTAwMDAwMDAwODMwNDBA5Lit5Zu96ZO26IGU6IKh5Lu95pyJ6ZmQ
5YWs5Y+4QDAwMDE2NDkzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
tXclo3H4pB+Wi4wSd0DGwnyZWni7+22Tkk6lbXQErMNHPk84c8DnjT8CW8jIfv3z
d5NBpvG3O3jQ/YHFlad39DdgUvqDd0WY8/C4Lf2xyo0+gQRZckMKEAId8Fl6/rPN
HsbPRGNIZgE6AByvCRbriiFNFtuXzP4ogG7vilqBckGWfAYaJ5zJpaGlMBOW1Ti3
MVjKg5x8t1/oFBkpFVsBnAeSGPJYrBn0irfnXDhOz7hcIWPbNDoq2bJ9VwbkKhJq
Vz7j7116pziUcLSFJasnWMnp8CrISj52cXzS/Y1kuaIMPP/1B0pcjVqMNJjowooD
OxID3TZGfk5V7S++4FowVwIDAQABo4HoMIHlMB8GA1UdIwQYMBaAFNHb6YiC5d0a
j0yqAIy+fPKrG/bZMEgGA1UdIARBMD8wPQYIYIEchu8qAQEwMTAvBggrBgEFBQcC
ARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wNwYDVR0fBDAw
LjAsoCqgKIYmaHR0cDovL2NybC5jZmNhLmNvbS5jbi9SU0EvY3JsMjI3Mi5jcmww
CwYDVR0PBAQDAgPoMB0GA1UdDgQWBBTEIzenf3VR6CZRS61ARrWMto0GODATBgNV
HSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAHMgTi+4Y9g0yvsUA
p7MkdnPtWLS6XwL3IQuXoPInmBSbg2NP8jNhlq8tGL/WJXjycme/8BKu+Hht6lgN
Zhv9STnA59UFo9vxwSQy88bbyui5fKXVliZEiTUhjKM6SOod2Pnp5oWMVjLxujkk
WKjSakPvV6N6H66xhJSCk+Ref59HuFZY4/LqyZysiMua4qyYfEfdKk5h27+z1MWy
nadnxA5QexHHck9Y4ZyisbUubW7wTaaWFd+cZ3P/zmIUskE/dAG0/HEvmOR6CGlM
55BFCVmJEufHtike3shu7lZGVm2adKNFFTqLoEFkfBO6Y/N6ViraBilcXjmWBJNE
MFF/yA==
-----END CERTIFICATE-----';
        }
//        $this->primaryKey = Config::model()->get('alipay_private_key');
//        $this->partner = Config::model()->get('alipay_partner');
//        $this->sellId = Config::model()->get('alipay_seller_id');

//        $this->createAbsoluteUrl('/order/notify/alipay');
//        switch ($this->_paymentType) {
//            case self::PAYMENT_TYPE_ALIPAY:
//                break;
//            case self::PAYMENT_TYPE_ALIPAY_WAP:
//                break;
//            case self::PAYMENT_TYPE_WECHAT:
////                Yii::import('ext.payment.AliPa');
//                break;
//            default:
//                $this->setErrors(array(201, '不支持的支付方式'));
//                break;
//        }
    }

    public function orderQueryWx($params)
    {
        $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $result = false;
        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['out_trade_no'] = $params['order_sn'];
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);

        $log = array();
        $log['url'] = $url;
        $log['request'] = $xml;
        $log['response'] = $data;
        file_put_contents(Yii::app()->getRuntimePath() . '/pay_' . date("Y_W") . '.txt', sprintf("%s %s %s", date("Y-m-d H:i:s"), json_encode($log), PHP_EOL), FILE_APPEND);

        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                if ($response['result_code'] == 'SUCCESS') {
                    if ($response['trade_state'] == 'SUCCESS') {
                        $result = true;
                    }
                } else {
                    $this->setErrors(array(301, $response['err_code_des']));
                }
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
//        公众账号ID    appid   是   String(32)  wxd678efh567hg6787  微信分配的公众账号ID（企业号corpid即为此appId）
//        商户号   mch_id  是   String(32)  1230000109  微信支付分配的商户号
//        微信订单号     transaction_id  二选一     String(32)  1009660380201506130728806387    微信的订单号，优先使用
//        商户订单号     out_trade_no    String(32)  20150806125346  商户系统内部的订单号，当没提供transaction_id时需要传这个。
//        随机字符串     nonce_str   是   String(32)  C380BEC2BFD727A4B6845133519F3AD6    随机字符串，不长于32位。推荐随机数生成算法
//        签名    sign    是   String(32)  5K8264ILTKCH16CQ2502SI8ZNMTM67VS    签名，详见签名生成算法
//        返回状态码     return_code     是   String(16)  SUCCESS
//
//SUCCESS/FAIL
//
//此字段是通信标识，非交易标识，交易是否成功需要查看trade_state来判断
//返回信息  return_msg  否   String(128)     签名失败
//
//返回信息，如非空，为错误原因
//
//签名失败
//
//参数格式校验错误
    }

    public function orderQueryUnion($params)
    {
        $result = false;
//        $url = 'https://101.231.204.80:5000/jiaofei/api/appTransReq.do';
        if ($this->debug) {
            $url = 'https://101.231.204.80:5000/gateway/api/appTransReq.do';
        } else {
            $url = 'https://gateway.95516.com/gateway/api/appTransReq.do';
        }

        $request = array();
        # 基本信息
        $request['version'] = '5.0.0'; # 版本号    version NS5 M   固定填写5.0.0   固定填写5.0.0
        $request['encoding'] = 'utf-8'; # 编码方式  encoding    ANS1..20    M   填写报文使用的字符编码，支持UTF-8与GBK编码   支持UTF-8、GBK
        $request['certId'] = UnionPayUtils::getSignCertId($this->cert_path, $this->cert_pwd); # 证书ID    certId  N1..128 M   填写签名私钥证书的Serial Number，该值可通过SDK获取   SDK代码从证书中读取
        $request['signMethod'] = '01'; # 签名方法   signMethod  N1..12  M   01：表示采用RSA  固定填写01
        $request['txnType'] = '00'; # 交易类型  txnType N2  M   取值：00：查询交易，01：消费，02：预授权，03：预授权完成，04：退货，05：圈存，11：代收，12：代付，13：账单支付，14：转账（保留），21：批量交易，22：批量查询，31：消费撤销，32：预授权撤销，33：预授权完成撤销，71：余额查询，72：实名认证-建立绑定关系，73：账单查询，74：解除绑定关系，75：查询绑定关系，77：发送短信验证码交易，78：开通查询交易，79：开通交易，94：IC卡脚本通知 固定填写01
        $request['txnSubType'] = '00'; # 交易子类   txnSubType  N2  M   依据实际交易类型填写。 固定填写01
        $request['bizType'] = '000201';//  # 产品类型   bizType N6  M   取值：000101：基金业务之股票基金；000102：基金业务之货币基金；000201：B2C网关支付；000301：无跳转（商户侧）；000302：评级支付；000401：代付；000501：代收；000601：账单支付；000801：无跳转（机构侧）；000901：绑定支付；000902: Token支付；001001：订购；000202：B2B 除以上产品外其他接口默认送000000，对账文件下载接口必送000000   固定填写000201
//        $request['bizType'] = '000000';
        # 商户信息
        $request['accessType'] = '0'; # 接入类型    accessType  N1  M   0：商户直连接入1：收单机构接入    固定填写0
        $request['merId'] = $this->mer_Id; # 商户代码   merId   AN15    M   已被批准加入银联互联网系统的商户代码  如已签约，请使用真实商户号。如未签约，可以在本平台右上角点我的测试-测试参数获取测试环境商户号

        # 订单信息
        /* 商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则 */
        $request['orderId'] = $params["order_sn"];    # 商户订单号   orderId AN8..32 M   商户订单号，仅能用大小写字母与数字，不能用特殊字符   商户端生成，例：12345asdf
        $request['txnTime'] = isset($params['pay_time']) ? $params['pay_time'] : date('YmdHis'); # 5    支付超时时间  payTimeout  YYYYMMDDHHmmss  O   超过此时间后，除网银交易外，其他交易银联系统会拒绝受理，提示超时。 跳转银行网银交易如果超时后交易成功，会自动退款，大约5个工作日金额返还到持卡人账户。 此时间建议取支付时的北京时间加15分钟。   订单支付超时时间，例：20151118101505。超过此时间未完成支付时银联页面会提示超时，不允许后续支付

        // 处理证书
        $request['signature'] = UnionPayUtils::sign($request, $this->cert_path, $this->cert_pwd); # 签名  signature   ANS1..1024  M   填写对报文摘要的签名，可通过SDK生成签名   SDK代码调用签名函数时自动计算

//        $response = array();
//        $response['respCode'];
//        $response['respMsg'];

        $opts = UnionPayUtils::createLinkString($request, false, true);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $opts);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
//        print_r($html);
        parse_str($html, $result);
//        var_dump($result);
        if ($result['respCode'] != '00') {
            $this->setErrors(array($result['respCode'], $result['respMsg']));
            $result = false;
        } else if ($result['origRespCode'] == '00') {
            $result = true;
        }
        return $result;
    }

    public function orderQueryAlipay($params)
    {
        $result = false;
        $url = "https://mapi.alipay.com/gateway.do";
        $url = "https://mapi.alipay.com/gateway.do?_input_charset=utf-8";

        $request = array();
        $request['service'] = 'single_trade_query';
        $request['partner'] = $this->partner;
        $request['_input_charset'] = 'utf-8';
        if (isset($params['trade_no'])) {
            $request['trade_no'] = $params['trade_no'];
        }
        $request['out_trade_no'] = $params['order_sn'];

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            $arg .= $key . "=" . $val . "&";
        }
        $arg = substr($arg, 0, count($arg) - 2);
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);
        $request['sign'] = $sign;
        $request['sign_type'] = 'RSA';

//        $request['sign'] = md5($arg . 'kzkjxozrps3g01ns2ncwtt15gpq47itl');
//        $request['sign_type'] = 'MD5';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);// 过滤HTTP头
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($ch, CURLOPT_POST, true);// post传输数据
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request);// post传输数据
        $responseText = curl_exec($ch);
        //unset($order_info);
        $ret = WxPayUtils::FromXml($responseText);
        if ($ret['is_success'] == "F") {
            $this->setErrors(array(991, $ret['error']));
        } else {
            if ($ret['response']['trade_status'] == 'TRADE_FINISHED' || $ret['response']['trade_status'] == 'TRADE_SUCCESS') {
                $result = true;
            }
        }
        curl_close($ch);
        return $result;

//        $url = "https://openapi.alipay.com/gateway.do";
//        $request = array();
//        $request['app_id'] = '2016020401138317';
//        $request['method'] = 'alipay.trade.query';
//        $request['format'] ='JSON';
//        $request['charset'] = 'utf-8';
//        $request['sign_type'] = 'RSA';
//        $request['timestamp'] = date('Y-m-d H:i:s');
//        $request['version'] = '1.0';
//        $request['out_trade_no'] = $params['order_sn'];
//
//        ksort($request);
//        reset($request);
//
//        $arg = "";
//        while (list ($key, $val) = each($request)) {
//            $arg .= $key . "=" . $val . "&";
//        }
//        //去掉最后一个&字符
//        $arg = substr($arg, 0, count($arg) - 2);
//
//        //如果存在转义字符，那么去掉转义
//        if (get_magic_quotes_gpc()) {
//            $arg = stripslashes($arg);
//        }
//
//        //$priKey = file_get_contents(Yii::app()->runtimePath . '/rsa_private_key.pem');
//        $res = openssl_get_privatekey($this->primaryKey);
//        openssl_sign($arg, $sign, $res);
//        openssl_free_key($res);
//        $sign = base64_encode($sign);
//        $request['sign'] = $sign;
//        //echo $sign;
//        //echo '<hr>';
//        //echo urldecode(AliPay::getHttpResponsePOST('http://wappaygw.alipay.com/service/rest.htm?', $para));exit;
//
//        $ch = curl_init();
//        curl_setopt($ch, CURLOPT_URL, $url);
//        curl_setopt($ch, CURLOPT_HEADER, 0);// 过滤HTTP头
//        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
//        curl_setopt($ch, CURLOPT_POST, true);// post传输数据
//        curl_setopt($ch, CURLOPT_POSTFIELDS, $request);// post传输数据
//        $responseText = curl_exec($ch);
//        //unset($order_info);
//        curl_close($ch);
//        var_dump($responseText);
        return false;
    }

    /**
     * @param $type
     * @param array $params
     */
    public function orderQuery($type, $params = array())
    {
        switch ($type) {
            case self::PAYMENT_TYPE_ALIPAY:
                return $this->orderQueryAlipay($params);
                break;
            case self::PAYMENT_TYPE_UNION:
                return $this->orderQueryUnion($params);
                break;
            case self::PAYMENT_TYPE_WECHAT:
                return $this->orderQueryWx($params);
                break;
            case self::PAYMENT_TYPE_PAYECO:
                return $this->orderQueryPayeco($params);
                break;
            case self::PAYMENT_TYPE_LLPAY:
                return $this->orderQueryLlpay($params);
                break;
            default:
                $this->setErrors(array(201, '不支持的支付方式'));
                break;
        }
    }

    public function verifySign($type, $data, $sign_type = 'rsa')
    {
        $result = false;
        switch ($type) {
            case self::PAYMENT_TYPE_ALIPAY:
                $para_filter = AliPayUtils::paraFilter($data);
                $para_sort = AliPayUtils::argSort($para_filter);
                $prestr = AliPayUtils::createLinkstring($para_sort);
                $res = openssl_get_publickey($this->pubKey);
                $result = (bool)openssl_verify($prestr, base64_decode($data['sign']), $res);
                openssl_free_key($res);
                break;
            case self::PAYMENT_TYPE_WECHAT:
                $xml = WxPayUtils::FromXml($data);
                $result = WxPayUtils::CheckSign($xml, $this->key);
                break;
            case self::PAYMENT_TYPE_SWIFTPAY:
                // TODO:
                $xml = WxPayUtils::FromXml($data);
                $result = WxPayUtils::CheckSign($xml, $this->swiftpass_key);
                break;
            case self::PAYMENT_TYPE_UNION:
                $public_key = UnionPayUtils::getPulbicKeyByCertId($this->union_publicKey, $data['certId']);
                $signature_str = $data['signature'];
                unset($data['signature']);
                $params_str = UnionPayUtils::createLinkString($data, true, false);
                $signature = base64_decode($signature_str);
                $params_sha1x16 = sha1($params_str, false);
                if ($public_key != null) {
                    $result = (bool)openssl_verify($params_sha1x16, $signature, $public_key, OPENSSL_ALGO_SHA1);
                } else {
                    $this->setErrors(array(112, 'union public key cert not match'));
                }
                break;
            case self::PAYMENT_TYPE_PAYECO:
                $extData = str_replace(" ", "+", $data['ExtData']);
                $extData = base64_decode($extData);
                $params_str = sprintf("Version=%s&MerchantId=%s&MerchOrderId=%s&Amount=%s&ExtData=%s&OrderId=%s&Status=%s&PayTime=%s&SettleDate=%s", $data['Version'], $data['MerchantId'], $data['MerchOrderId'], $data['Amount'], $extData, $data['OrderId'], $data['Status'], $data['PayTime'], $data['SettleDate']);
                if ($this->debug) {
                    $pubKeyStr = $this->payeco_test_publicKey;
                } else {
                    $pubKeyStr = $this->payeco_publicKey;
                }
                $result = PayecoUtils::verify($params_str, $data['Sign'], $pubKeyStr);
                break;
            case self::PAYMENT_TYPE_LLPAY:
                $request = json_decode($data, true);
                $sign = $request['sign'];
                unset($request['sign']);

                ksort($request);
                reset($request);
                $arg = "";
                while (list ($key, $val) = each($request)) {
                    if (!empty($val)) {
                        $arg .= $key . "=" . $val . "&";
                    }
                }
                // 追加md5签名key
                $arg .= 'key=' . $this->llpay_merchant_key;
                $result = (md5($arg) == $sign);
                break;
            default:
                break;
        }
        return $result;
    }

    /**
     * 构建支付信息
     *
     * @param $type
     * @param $params
     * @param array $extra
     * @return string
     */
    public function buildPayInfo($type, $params, $extra = array())
    {
        // 0. 检查是否配置支付信息
        // 1. 检查传入业务参数
        // 2. 调用相应方法
        // 3. 返回支付信息 或 设置错误信息
        $result = '';
        if (!empty($params)) {
            switch ($this->_paymentType) {
                case self::PAYMENT_TYPE_ALIPAY:
                    $this->setErrors(array(201, '没有配置支付信息'));
                    $this->setErrors(array(301, '支付方式缺少必填参数'));
                    $result = $this->buildPayInfoAliPay($params, $extra);
                    break;
                case self::PAYMENT_TYPE_WECHAT:
                    // 检查是否配置参数
                    // 检查传入业务参数
                    $this->setErrors(array(201, '没有配置支付信息'));
                    $this->setErrors(array(301, '支付方式缺少必填参数'));
                    $result = $this->buildPayInfoAliPay($params, $extra);
                    break;
                case self::PAYMENT_TYPE_ALIPAY_WAP:
                    // TODO: 待调试
                    $tokenInfo = $this->_buildWapPayInfo($params);
                    $result = $this->buildPayInfoAliPayWap($tokenInfo);
                    break;
                default:
                    break;
            }
        } else {
            $this->setErrors(array(101, '没有传入支付所需参数'));
        }
        return $result;
    }

    /**
     * 其他回调预处理方法, 统一处理参数之后, 返回统一信息
     *
     * @param $type
     * @param array $params
     * @return array
     * @throws WxPayException
     */
    public function verifyCallback($type, $params = array())
    {
        // 0. 获取全部参数, 并保存下来
        // 1. 抽取数据
        // 2. 验证签名
        // 3. 判断支付结果
        // 4. 返回订单相关信息 或者 设置错误码
        $result = array();
        if (empty($params)) {
            $params = array();
            $params['server'] = $_SERVER;
            $params['post'] = $_POST;
            $params['get'] = $_GET;
            $params['request'] = $_REQUEST;
            $params['raw'] = file_get_contents("php://input");
            file_put_contents(Yii::app()->getRuntimePath() . '/callback_' . date("Y_W") . '.txt', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), $type, json_encode($params), PHP_EOL), FILE_APPEND);
        }
        if ($type == self::PAYMENT_TYPE_WECHAT) {
            $notify = WxPayUtils::FromXml($params['raw']);
            // TODO: verify
//            if (!$this->verifySign($type, $params['raw'])) {
//                $this->setErrors(array(403, '非法请求'));
//                return;
//            }
//            if ($notify['result_code'] == 'SUCCESS') {
            $result['product_id'] = $notify['product_id'];
            $result['openid'] = $notify['openid'];
            $result['nonce_str'] = $notify['nonce_str'];
            $result['is_subscribe'] = $notify['is_subscribe'];
            $result['mch_id'] = $notify['mch_id'];
            $result['appid'] = $notify['appid'];
            $result['raw'] = $params['raw'];
//            } else {
//                $this->setErrors(array(401, '错误的回调'));
//            }
        } else {
            $this->setErrors(array(1111, "不支持的回调通知, 请联系开发商"));
        }
        return $result;
    }

    public function verifyRefund($type, $params = array())
    {
        // 0. 获取全部参数, 并保存下来
        // 1. 抽取数据
        // 2. 验证签名
        // 3. 判断支付结果
        // 4. 返回订单相关信息 或者 设置错误码
        $result = array();
        if (empty($params)) {
            $params = array();
            $params['server'] = $_SERVER;
            $params['post'] = $_POST;
            $params['get'] = $_GET;
            $params['request'] = $_REQUEST;
            $params['raw'] = file_get_contents("php://input");
            file_put_contents(Yii::app()->getRuntimePath() . '/refund_' . date("Y_W") . '.txt', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), $type, json_encode($params), PHP_EOL), FILE_APPEND);
        }
        if ($type == self::PAYMENT_TYPE_ALIPAY) {
            // TODO: 验证是否是支付宝发来的通知, 调用notify_id, 请求支付宝验证接口 检验是否合法
            // TODO: 目前暂时只支持单笔
            if (!$this->verifySign($type, $params['post'])) {
                $this->setErrors(array(403, '非法请求, 签名验证失败'));
                return;
            }
            $params['post']['sign_type'];
            $params['post']['sign'];
            if ($params['post']['notify_type'] == 'batch_refund_notify') {
                foreach (explode('|', $params['post']['result_details']) as $value) {
                    $detail = explode('^', $value);
                    $result['order_sn'] = $detail[0];
                    $result['amount'] = $detail[1];
                    $result['custom'] = $detail[2];
                    $result['query_id'] = $params['post']['batch_no'];
                    if ($detail[2] != 'SUCCESS') {
                        $this->setErrors(array(401, '支付未成功'));
                        return;
                    }
                }
                $result['raw'] = $params['post'];
                return $result;
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_UNION) {
            if (!$this->verifySign($type, $params['post'])) {
                $this->setErrors(array(403, '非法请求, 签名验证失败'));
                return;
            }
            $params['post']['signMethod'];
            $params['post']['signature'];
            if ($params['post']['respCode'] == '00') {
                $result['order_sn'] = $params['post']['orderId'];
                $result['amount'] = $params['post']['txnAmt'] / 100;
                $result['query_id'] = $params['post']['queryId'];
                $result['custom'] = isset($params['post']['reqReserved']) ? $params['post']['reqReserved'] : '';
                $result['raw'] = $params['post'];
                return $result;
            } else {
                $this->setErrors(array($params['post']['respCode'], $params['post']['respMsg']));
            }
        } else if ($type == self::PAYMENT_TYPE_LLPAY) {
//            if (!$this->verifySign($type, $params['raw'])) {
//                $this->setErrors(array(403, '非法请求, 签名验证失败'));
//                return;
//            }
            $ret = json_decode($params['raw'], true);
            if ($ret['sta_refund'] == '2') {
                $result['refund_sn'] = $ret['no_refund'];
                $result['refund_amount'] = $ret['money_refund'];
                $result['query_id'] = $ret['oid_refundno'];
                $result['raw'] = $params['raw'];
                return $result;
            } else {
                $this->setErrors(array(111, '退款失败'));
            }
            // echo {"ret_code":"0000", "ret_msg":"交易成功"}
        } else {
            $this->setErrors(array(1111, "不支持的退款通知, 请联系开发商"));
        }
    }

    /**
     * 验证付款方法
     *
     * @param $type
     * @param array $params
     * TODO:
     * @return array
     */
    public function verifyTransfer($type, $params = array())
    {
        // 0. 获取全部参数, 并保存下来
        // 1. 抽取数据
        // 2. 验证签名
        // 3. 判断支付结果
        // 4. 返回订单相关信息 或者 设置错误码
        $result = array();
        if (empty($params)) {
            $params = array();
            $params['server'] = $_SERVER;
            $params['post'] = $_POST;
            $params['get'] = $_GET;
            $params['request'] = $_REQUEST;
            $params['raw'] = file_get_contents("php://input");
            file_put_contents(Yii::app()->getRuntimePath() . '/notify_' . date("Y_W") . '.txt', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), $type, json_encode($params), PHP_EOL), FILE_APPEND);
        }
        if ($type == self::PAYMENT_TYPE_UNION) {
            if (!$this->verifySign($type, $params['post'])) {
                $this->setErrors(array(403, '非法请求, 签名验证失败'));
                return;
            }
            $params['post']['signMethod'];
            $params['post']['signature'];
            if ($params['post']['respCode'] == '00') {
                $result['order_sn'] = $params['post']['orderId'];
                $result['amount'] = $params['post']['txnAmt'] / 100;
                $result['custom'] = isset($params['post']['reqReserved']) ? $params['post']['reqReserved'] : '';
                $result['raw'] = $params['post'];
            } else {
                $this->setErrors(array($params['post']['respCode'], $params['post']['respMsg']));
            }
        } else {
            $this->setErrors(array(1111, "不支持的支付通知, 请联系开发商"));
        }
        return $result;
    }

    /**
     * 支付通知预处理方法, 统一处理参数之后, 返回统一信息
     *
     * @param $type
     * @param array $params
     * @return array
     * @throws WxPayException
     */
    public function verifyNotify($type, $params = array())
    {
        // 0. 获取全部参数, 并保存下来
        // 1. 抽取数据
        // 2. 验证签名
        // 3. 判断支付结果
        // 4. 返回订单相关信息 或者 设置错误码
        $result = array();
        if (empty($params)) {
            $params = array();
            $params['server'] = $_SERVER;
            $params['post'] = $_POST;
            $params['get'] = $_GET;
            $params['request'] = $_REQUEST;
            $params['raw'] = file_get_contents("php://input");
            file_put_contents(Yii::app()->getRuntimePath() . '/notify_' . date("Y_W") . '.txt', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), $type, json_encode($params), PHP_EOL), FILE_APPEND);
        }
        if ($type == self::PAYMENT_TYPE_WECHAT) {
            $notify = WxPayUtils::FromXml($params['raw']);
            // TODO: verify
            if (!$this->verifySign($type, $params['raw'])) {
                $this->setErrors(array(403, '非法请求'));
                return;
            }
            if ($notify['result_code'] == 'SUCCESS') {
                $result['order_sn'] = $notify['out_trade_no'];
                $result['amount'] = $notify['total_fee'] / 100;
                $result['query_id'] = $notify['transaction_id'];
                $result['custom'] = isset($notify['attach']) ? $notify['attach'] : '';
                $result['raw'] = $params['raw'];
//                var_dump($result);
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_ALIPAY) {
            // TODO: 验证是否是支付宝发来的通知, 调用notify_id, 请求支付宝验证接口 检验是否合法
            // TODO: verify
            if (!$this->verifySign($type, $params['post'])) {
                $this->setErrors(array(403, '非法请求, 签名验证失败'));
                return;
            }
            $params['post']['sign_type'];
            $params['post']['sign'];
            if ($params['post']['trade_status'] == 'TRADE_FINISHED' || $params['post']['trade_status'] == 'TRADE_SUCCESS') {
                $result['order_sn'] = $params['post']['out_trade_no'];
                $result['amount'] = $params['post']['total_fee'];
                $result['query_id'] = $params['post']['trade_no'];
                $result['custom'] = isset($params['post']['extra_common_param']) ? $params['post']['extra_common_param'] : '';
                $result['raw'] = $params['post'];
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_ALIPAY_WAP) {
            // TODO: 验证新接口
            $doc = new DOMDocument();
            $doc->loadXML(AliPayUtils::rsaDecrypt($params['post']['notify_data'], $this->primaryKey));
            if (!empty($doc->getElementsByTagName('notify')->item(0)->nodeValue)) {
                $out_trade_no = $doc->getElementsByTagName('out_trade_no')->item(0)->nodeValue;
                $trade_status = $doc->getElementsByTagName('trade_status')->item(0)->nodeValue;
                if ($trade_status == 'TRADE_FINISHED' || $trade_status == 'TRADE_SUCCESS') {
                    $result['order_sn'] = $out_trade_no;
                    $result['amount'] = $params['post']['total_fee'];
                    $result['query_id'] = $params['post']['trade_no'];
                    $result['custom'] = isset($params['post']['extra_common_param']) ? $params['post']['extra_common_param'] : '';
                    $result['raw'] = $params['post'];
                } else {
                    $this->setErrors(array(401, '支付未成功'));
                }
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_UNION) {
            if (!$this->verifySign($type, $params['post'])) {
                $this->setErrors(array(403, '非法请求, 签名验证失败'));
                return;
            }
            $params['post']['signMethod'];
            $params['post']['signature'];
            if ($params['post']['respCode'] == '00') {
                $result['order_sn'] = $params['post']['orderId'];
                $result['amount'] = $params['post']['txnAmt'] / 100;
                $result['query_id'] = $params['post']['queryId'];
                $result['custom'] = isset($params['post']['reqReserved']) ? $params['post']['reqReserved'] : '';
                $result['raw'] = $params['post'];
            } else {
                $this->setErrors(array($params['post']['respCode'], $params['post']['respMsg']));
            }
        } else if ($type == self::PAYMENT_TYPE_LLPAY) {
            if (isset($params['post']['result_pay'])) {
                $notify = $params['post'];
            } else {
                $notify = json_decode($params['raw'], true);
            }
            // TODO: verify
//            if (!$this->verifySign($type, $params['raw'])) {
//                $this->setErrors(array(403, '非法请求'));
//                return;
//            }
            if ($notify['result_pay'] == 'SUCCESS') {
                $result['order_sn'] = $notify['no_order'];
                $result['amount'] = $notify['money_order'];
                $result['query_id'] = $notify['oid_paybill'];
                $result['custom'] = isset($notify['info_order']) ? $notify['info_order'] : '';
                // 这里有问题, 因为可能需要保存post
                $result['raw'] = $params['raw'];
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_SWIFTPAY) {
            $notify = WxPayUtils::FromXml($params['raw']);
            if (!$this->verifySign($type, $params['raw'])) {
                $this->setErrors(array(403, '非法请求'));
                return;
            }
            if ($notify['result_code'] == '0' && $notify['pay_result'] == '0') {
                $result['order_sn'] = $notify['out_trade_no'];
                $result['amount'] = $notify['total_fee'] / 100;
                $result['query_id'] = $notify['transaction_id'];
                $result['custom'] = isset($notify['attach']) ? $notify['attach'] : '';
                $result['raw'] = $params['raw'];
//                var_dump($result);
            } else {
                $this->setErrors(array(401, '支付未成功'));
            }
        } else if ($type == self::PAYMENT_TYPE_PAYECO) {
            // 验证方式
            if (isset($params['request']['response_text'])) {
//                $verifyResult = $this->verifySign($type, $params['get']);
//            var_dump($verifyResult);
//            if (!$verifyResult) {
//                $this->setErrors(array(403, '非法请求, 签名验证失败'));
//                return;
//            }
//                $privatKey = '123456';//商户私钥

                $paynotify = $_REQUEST['response_text'];
//                $paynotify = urldecode($paynotify);
                $xml = base64_decode($paynotify);

//                print_r($xml);
                $ret = simplexml_load_string($xml);
//                $ret = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
//                print_r($ret);

                //组建令牌
                $macClear = '';
                $macClear .= !empty($ret->ProcCode) ? $ret->ProcCode . ' ' : '';
                $macClear .= !empty($ret->AccountNo) ? $ret->AccountNo . ' ' : '';
                $macClear .= !empty($ret->ProcessCode) ? $ret->ProcessCode . ' ' : '';
                $macClear .= !empty($ret->Amount) ? $ret->Amount . ' ' : '';
                $macClear .= !empty($ret->TransDatetime) ? $ret->TransDatetime . ' ' : '';
                $macClear .= !empty($ret->AcqSsn) ? $ret->AcqSsn . ' ' : '';
                $macClear .= !empty($ret->OrderNo) ? $ret->OrderNo . ' ' : '';
                $macClear .= !empty($ret->TransData) ? $ret->TransData . ' ' : '';
                $macClear .= !empty($ret->Reference) ? $ret->Reference . ' ' : '';
                $macClear .= !empty($ret->RespCode) ? $ret->RespCode . ' ' : '';
                $macClear .= !empty($ret->TerminalNo) ? $ret->TerminalNo . ' ' : '';
                $macClear .= !empty($ret->MerchantNo) ? $ret->MerchantNo . ' ' : '';
                $macClear .= !empty($ret->MerchantOrderNo) ? $ret->MerchantOrderNo . ' ' : '';
                $macClear .= !empty($ret->OrderState) ? $ret->OrderState . ' ' : '';
                $macClear = trim($macClear);
//                $mac = md5(strtoupper($macClear) . " " . 'CE4AAD5ACF9E414A');
                $mac = md5(strtoupper($macClear) . " " . $this->payeco_web_merchant_key);  //mac
                $mac = strtoupper($mac);

                // 验证令牌
                // TODO: 临时禁用验证
//                if ($ret->MAC != $mac) {
//                    $this->setErrors(array(111, '验证失败'));
//                    return;
//                }

                // 验证交易
                if ($ret->OrderState != '02' || $ret->RespCode != '0000') {
                    $this->setErrors(array(222, '交易失败'));
                    return;
                }

                $result['order_sn'] = $ret->MerchantOrderNo;
                $result['amount'] = $ret->Amount;
                // TODO: query_id
                $result['custom'] = isset($ret->Reference) ? $ret->Reference : '';
                $result['raw'] = $ret;
            } else if ($params['request']['Status'] == '02') {
//                $verifyResult = $this->verifySign($type, $params['request']);
//            var_dump($verifyResult);
//            if (!$verifyResult) {
//                $this->setErrors(array(403, '非法请求, 签名验证失败'));
//                return;
//            }
                $result['order_sn'] = $params['request']['MerchOrderId'];
                $result['amount'] = $params['request']['Amount'];
                // TODO: query_id
                $result['custom'] = isset($params['request']['ExtData']) ? $params['request']['ExtData'] : '';
                $result['raw'] = $params['request'];
            } else {
                $this->setErrors(array($params['post']['respCode'], $params['post']['respMsg']));
            }
        } else if ($type == self::PAYMENT_TYPE_ALLINPAY) {
            $notify = $params['post'];
            return $this->verifyNotifyAllinpay($notify);
        } else {
            $this->setErrors(array(1111, "不支持的支付通知, 请联系开发商"));
        }
        return $result;
    }

    public function buildPayInfoAliPay($params, $extra = array())
    {
        $result = 'service="mobile.securitypay.pay"';
        $result .= '&partner="' . $this->partner . '"';
        $result .= '&_input_charset="utf-8"';
        $result .= '&out_trade_no="' . $params['order_sn'] . '"';
        $result .= '&subject="' . $params['product_name'] . '"';
        $result .= '&payment_type="1"';
        $result .= '&seller_id="' . $this->partner . '"';
        $result .= '&body="' . (isset($extra['product_intro']) ? $extra['product_intro'] : $params['product_name']) . '"';
        $result .= '&total_fee="' . $params['amount'] . '"';
//                    $result .= "&app_id=\"" . "1233" . "\"";
//                    $result .= "&appenv=\"" . "system=android^version=1" . "\"";
        $result .= "&notify_url=\"" . urlencode($params['notify_url']) . "\"";
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($result, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);
        $result .= '&sign="' . urlencode($sign) . '"';
        $result .= '&sign_type="RSA"';
        return $result;
    }

    public function buildPayInfoAliPayWeb($params, $extra = array())
    {
        $result = array();
        $result['url'] = 'https://mapi.alipay.com/gateway.do?_input_charset=utf-8';

        $request = array();
        $request['service'] = 'create_direct_pay_by_user';
        $request['partner'] = $this->partner;
        $request['_input_charset'] = 'utf-8';

        //$request['notify_url'] = urlencode($params['notify_url']);
        $request['notify_url'] = $params['notify_url'];
        if (isset($extra['return_url'])) {
            //$request['return_url'] = urlencode($extra['return_url']);
            $request['return_url'] = $extra['return_url'];
        }

        $request['out_trade_no'] = $params['order_sn'];
        $request['subject'] = $params['product_name'];
        $request['payment_type'] = 1;
        $request['total_fee'] = $params['amount'];
//        $request['seller_id'] = $this->sellId;
        $request['seller_email'] = $this->sellId;
//        $request['seller_account_name'] = $this->sellId;
        ksort($request);
        reset($request);

        $para_filter = array();
        while (list ($key, $val) = each($request)) {
            if ($key == "sign" || $key == "sign_type" || $val == "") continue;
            else $para_filter[$key] = $request[$key];
        }
        $request = $para_filter;

        $arg = '';
        while (list ($key, $val) = each($request)) {
            $arg .= $key . '=' . $val . '&';
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);

        $request['sign'] = $sign;
        $request['sign_type'] = 'RSA';

        $result['params'] = $request;
        return $result;
    }

    public function buildPayInfoAliPayWapNew($params, $extra = array())
    {
        // TODO :
        $result = array();
        $result['url'] = 'https://mapi.alipay.com/gateway.do?_input_charset=utf-8';

        $request = array();
        $request['service'] = 'alipay.wap.create.direct.pay.by.user';
        $request['partner'] = $this->partner;
        $request['_input_charset'] = 'utf-8';

        //$request['notify_url'] = urlencode($params['notify_url']);
        $request['notify_url'] = $params['notify_url'];
        if (isset($extra['return_url'])) {
            //$request['return_url'] = urlencode($extra['return_url']);
            $request['return_url'] = $extra['return_url'];
        }

        $request['out_trade_no'] = $params['order_sn'];
        $request['subject'] = $params['product_name'];
        $request['payment_type'] = 1;
        $request['total_fee'] = $params['amount'];
        $request['seller_id'] = $this->partner;
//        $request['seller_email'] = $this->sellId;
//        $request['seller_account_name'] = $this->sellId;
        ksort($request);
        reset($request);

        $para_filter = array();
        while (list ($key, $val) = each($request)) {
            if ($key == "sign" || $key == "sign_type" || $val == "") continue;
            else $para_filter[$key] = $request[$key];
        }
        $request = $para_filter;

        $arg = '';
        while (list ($key, $val) = each($request)) {
            $arg .= $key . '=' . $val . '&';
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);

        $request['sign'] = $sign;
        $request['sign_type'] = 'RSA';

        $result['params'] = $request;
        return $result;
    }

    public function buildPayInfoAliPayWap($tokenInfo)
    {
        $para = array();
        $para['service'] = 'alipay.wap.auth.authAndExecute';
        $para['partner'] = $this->partner;
        $para['format'] = 'xml';
        $para['v'] = '2.0';
        //$para['req_id'] = time();
        $para['sec_id'] = '0001';
        $para['_input_charset'] = 'utf-8';
        $para['req_data'] = '<auth_and_execute_req><request_token>' . $tokenInfo['request_token'] . '</request_token></auth_and_execute_req>';
        ksort($para);
        reset($para);

        $arg = '';
        while (list ($key, $val) = each($para)) {
            $arg .= $key . '=' . $val . '&';
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);

        $para = array();
        $para['service'] = 'alipay.wap.auth.authAndExecute';
        $para['partner'] = $this->partner;
        $para['format'] = 'xml';
        $para['v'] = '2.0';
        //$para['req_id'] = time();
        $para['sec_id'] = '0001';
        $para['_input_charset'] = 'utf-8';
        $para['req_data'] = urlencode('<auth_and_execute_req><request_token>' . $tokenInfo['request_token'] . '</request_token></auth_and_execute_req>');
        $arg = '';
        while (list ($key, $val) = each($para)) {
            $arg .= $key . '=' . $val . '&';
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        $result = $arg . '&sign=' . urlencode($sign);
        return $result;
    }

    private function _buildWapPayInfo($params)
    {
        $ret = "http://wappaygw.alipay.com/service/rest.htm?";
        $req_data = "<direct_trade_create_req><subject>%s</subject><out_trade_no>%s</out_trade_no><total_fee>%s</total_fee><seller_account_name>%s</seller_account_name><call_back_url>%s</call_back_url><notify_url>%s</notify_url></direct_trade_create_req>";

        $c_url = $this->createAbsoluteUrl('/order/notify/alipay_wap_success');
        $n_url = $this->createAbsoluteUrl('/order/notify/alipay_wap');
        $order_sn = $params['order_sn'];
        $amount = $params['amount'];
        $p_name = $params['product_name'];

        $para = array();
        $para['service'] = 'alipay.wap.trade.create.direct';
        $para['partner'] = $this->partner;
        $para['format'] = 'xml';
        $para['v'] = '2.0';
        $para['req_id'] = time();
        $para['sec_id'] = '0001';
        $para['req_data'] = sprintf($req_data, $p_name, $order_sn, $amount, $this->sellId, $c_url, $n_url);
        $para['_input_charset'] = 'utf-8';
        ksort($para);
        reset($para);

        $arg = "";
        while (list ($key, $val) = each($para)) {
            $arg .= $key . "=" . $val . "&";
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        //$priKey = file_get_contents(Yii::app()->runtimePath . '/rsa_private_key.pem');
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);
        $para['sign'] = $sign;
        //echo $sign;
        //echo '<hr>';
        //echo urldecode(AliPay::getHttpResponsePOST('http://wappaygw.alipay.com/service/rest.htm?', $para));exit;

        $ch = curl_init($ret);
        curl_setopt($ch, CURLOPT_HEADER, 0);// 过滤HTTP头
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($ch, CURLOPT_POST, true);// post传输数据
        curl_setopt($ch, CURLOPT_POSTFIELDS, $para);// post传输数据
        $responseText = curl_exec($ch);
        //unset($order_info);
        curl_close($ch);
        $s = AliPayUtils::parseResponse(urldecode($responseText), $this->primaryKey);
        return $s;
    }

    public function buildPayInfoAllinpayWeb($params, $extra = array())
    {
        if ($this->debug) {
            $url = 'http://ceshi.allinpay.com/gateway/index.do';
        } else {
            $url = 'https://service.allinpay.com/gateway/index.do';
        }

        $request = array();
        $request['inputCharset'] = "1";
        $request['pickupUrl'] = $params['return_url'];
        $request['receiveUrl'] = $params['notify_url'];
        $request['version'] = 'v1.0';
        $request['language'] = '1';
        $request['signType'] = '0';
        $request['merchantId'] = $this->allinpay_merchant_id;

        $request['orderNo'] = $params['order_sn'];//字符串，只允许使用字母、数字、-、_,并以字母或数字开头；每商户提交的订单号，必须在当天的该商户所有交易中唯一
        $request['orderAmount'] = $params['order_amount'] * 100;//cent
        $request['orderCurrency'] = 156;
        $request['orderDatetime'] = $params['order_time'];//date('YmdHis')
        //$request['orderExpireDatetime'] = '';//min
        //$request['productName'] = '';
        $request['payType'] = '0';

        $to_sign = '';
        foreach ($request as $key => $value) {
            $to_sign .= $key . '=' . $value . '&';
        }

        $request['signMsg'] = strtoupper(md5($to_sign . 'key=' . $this->allinpay_md5_key));

        $result = array();
        $result['url'] = $url;
        $result['params'] = $request;
        $result['method'] = 'POST';
        return $result;
    }

    public function buildAllinpayApp($params, $extra = array())
    {
        $url = 'http://ceshi.allinpay.com/usercenter/merchant/UserInfo/reg.do';
//$url = 'https://cashier.allinpay.com/usercenter/merchant/UserInfo/reg.do';

        $md5 = '1234567890';
        $merchant_id = '008310107420099';

        $parmas = array();
        $parmas['signType'] = '0';
        $parmas['merchantId'] = $merchant_id;
        $parmas['partnerUserId'] = '1111';

        var_dump('&' . http_build_query($parmas) . '&key=' . $md5 . '&');

        $parmas['signMsg'] = strtoupper(md5('&' . http_build_query($parmas) . '&key=' . $md5 . '&'));
        var_dump($parmas);
        var_dump(request($url, $parmas, 'POST'));

        /*
        merchantId
        signType
        userId
        resultCode
        returnDatetime
        signMsg
        */

        $notify_url = 'http://zhilebd.com/order/notify';
        $product_name = '爪子';
        $order_sn = time();
        $order_amount = '10';
        $order_date = date('Y-m-d H:i:s');
        $user_id = '111';

        $parmas['inputCharset'] = 1;
        $parmas['receiveUrl'] = $notify_url;
        $parmas['version'] = "v1.0";
//language=1'] = '';
        $parmas['signType'] = '1';
        $parmas['merchantId'] = $merchant_id;
        $parmas['orderNo'] = $order_sn;
        $parmas['orderAmount'] = $order_amount * 100;//cent
        $parmas['orderCurrency'] = 156;
        $parmas['orderDatetime'] = $order_date;
//orderExpireDatetime'] = '';//min
        $parmas['productName'] = $product_name;
        $parmas['ext1'] = "<USER>" . $user_id . "</USER>";
//ext2='';
//extTL
        $parmas['payType'] = 0;
//issuerId='';visa、mastercard
//tradeNature=GOODS或SERVICES

        var_dump(http_build_query($parmas) . '&key=' . $md5);

        $parmas['signMsg'] = strtoupper(md5(http_build_query($parmas) . '&key=' . $md5));
        var_dump(json_encode($parmas));
    }

    public function orderQueryAllinpay($params, $extra = array())
    {
        if ($this->debug) {
            $url = 'http://ceshi.allinpay.com/gateway/index.do';
        } else {
            $url = 'https://service.allinpay.com/gateway/index.do';
        }
        $request = array();
        $request['merchantId'] = $this->allinpay_merchant_id;
        $request['version'] = 'v1.5';
        $request['signType'] = '0';
        $request['orderNo'] = $params['order_sn'];
        $request['orderDatetime'] = $params['order_time'];
        $request['queryDatetime'] = date('YmdHis');
        $to_sign = '';
        foreach ($request as $key => $value) {
            $to_sign .= $key . '=' . $value . '&';
        }

        $request['signMsg'] = strtoupper(md5($to_sign . 'key=' . $this->allinpay_md5_key));
//        var_dump($url);
//        var_dump($request);

        $result = $this->request($url, $request, 'GET');
//        echo '<hr>';
        parse_str($result, $res);
//        var_dump($res);
        if (isset($res['ERRORCODE'])) {
            $this->setErrors(array($res['ERRORCODE'], $res['ERRORMSG']));
            return false;
        }
        if (empty($res['errorCode'])) {
            // TODO: sign
            if ($res['payResult'] == 1) {
                $res['orderNo'];
                $res['signMsg'];
                $res['merchantId'];
                if ($res['payAmount'] == $res['orderAmount']) {
                    return true;
                } else {
                    $this->setErrors(array(300, "支付金额不足"));
                }
            } else {
                $this->setErrors(array(300, "支付失败"));
            }
        } else {
            $this->setErrors(array(300, "支付失败"));
        }
        return $result;
    }

    public function refundAllinpay($params, $extra = array())
    {
        if ($this->debug) {
            $url = 'http://ceshi.allinpay.com/gateway/index.do';
        } else {
            $url = 'https://service.allinpay.com/gateway/index.do';
        }
        $request = array();
        $request['version'] = 'v2.3';
        $request['signType'] = '0';
        $request['merchantId'] = $this->allinpay_merchant_id;
        $request['orderNo'] = $params['order_sn'];
        $request['refundAmount'] = $params['refund_amount'] * 100;
        $request['mchtRefundOrderNo'] = $params['refund_sn'];
        $request['orderDatetime'] = $params['order_time'];
        $to_sign = '';
        foreach ($request as $key => $value) {
            $to_sign .= $key . '=' . $value . '&';
        }

        $request['signMsg'] = strtoupper(md5($to_sign . 'key=' . $this->allinpay_md5_key));
        $result = $this->request($url, $request, 'GET');
        parse_str($result, $ret);
        if (isset($ret['ERRORCODE'])) {
            $this->setErrors(array($ret['ERRORCODE'], $ret['ERRORMSG']));
            return false;
        } else {
            $to_sign = '';
            $to_sign = $to_sign . ($ret['merchantId'] != "" ? "merchantId=" . $ret['merchantId'] . "&" : "");
            $to_sign = $to_sign . ($ret['version'] != "" ? "version=" . $ret['version'] . "&" : "");
            $to_sign = $to_sign . ($ret['signType'] != "" ? "signType=" . $ret['signType'] . "&" : "");
            $to_sign = $to_sign . ($ret['orderNo'] != "" ? "orderNo=" . $ret['orderNo'] . "&" : "");
            $to_sign = $to_sign . ($ret['orderAmount'] != "" ? "orderAmount=" . $ret['orderAmount'] . "&" : "");
            $to_sign = $to_sign . ($ret['orderDatetime'] != "" ? "orderDatetime=" . $ret['orderDatetime'] . "&" : "");
            $to_sign = $to_sign . ($ret['refundAmount'] != "" ? "refundAmount=" . $ret['refundAmount'] . "&" : "");
            $to_sign = $to_sign . ($ret['refundDatetime'] != "" ? "refundDatetime=" . $ret['refundDatetime'] . "&" : "");
            $to_sign = $to_sign . ($ret['refundResult'] != "" ? "refundResult=" . $ret['refundResult'] . "&" : "");
            $to_sign = $to_sign . ($ret['mchtRefundOrderNo'] != "" ? "mchtRefundOrderNo=" . $ret['mchtRefundOrderNo'] . "&" : "");
            $to_sign = $to_sign . ($ret['returnDatetime'] != "" ? "returnDatetime=" . $ret['returnDatetime'] . "&" : "");

            if ($ret['signMsg'] == strtoupper(md5($to_sign . 'key=' . $this->allinpay_md5_key))) {
                if ($ret['refundResult'] == 20) {
//                $ret['orderNo'];
//                $ret['signMsg'];
//                $ret['merchantId'];
                    if ($ret['refundAmount'] == $ret['orderAmount']) {
                        return true;
                    } else {
                        $this->setErrors(array(300, "退款金额错误"));
                    }
                } else {
                    $this->setErrors(array(300, "退款失败"));
                }
            } else {
                $this->setErrors(array(300, "签名错误"));
            }
        }
    }

    public function queryRefundAllinpay($params, $extra = array())
    {
        if ($this->debug) {
            $url = 'http://ceshi.allinpay.com/mchtoq/refundQuery';
        } else {
            $url = 'https://service.allinpay.com/mchtoq/refundQuery';
        }
        $request = array();
        $request['version'] = 'v2.4';
        $request['signType'] = '0';
        $request['merchantId'] = $this->allinpay_merchant_id;
        $request['orderNo'] = $params['order_sn'];
        $request['refundAmount'] = $params['refund_amount'] * 100;
//        $request['refundDatetime'] = $params['refund_time'];
        $request['mchtRefundOrderNo'] = $params['refund_sn'];
        $to_sign = '';
        foreach ($request as $key => $value) {
            $to_sign .= $key . '=' . $value . '&';
        }

        $request['signMsg'] = strtoupper(md5($to_sign . 'key=' . $this->allinpay_md5_key));
        $result = $this->request($url, $request);
        parse_str($result, $err);
        // ERRORCODE=005&ERRORMSG=订单号不存在
        if (!isset($err['ERRORCODE'])) {
            $full = explode("\r\n", $result);
            if ($full[1] == strtoupper(md5($full[0] . '|' . $this->allinpay_md5_key))) {
                $ret = explode('|', $full[0]);
                $version = $ret[0];
                $signType = $ret[1];
                $merchantId = $ret[2];
                $orderNo = $ret[3];
                $refundAmount = $ret[4];
                $refunDatetime = $ret[5];
                $mchtRefundOrderNo = $ret[6];
                $refundResult = $ret[7];
                $returnDatetime = $ret[8];
                if ($refundResult == 'TKSUCC0006') {
                    return true;
                } else {
                    $code = array(
                        'TKSUCC0001'=>'退款未受理',
                        'TKSUCC0002'=>'待通联审核',
                        'TKSUCC0003'=>'通联审核通过',
                        'TKSUCC0004'=>'退款冲销',
                        'TKSUCC0005'=>'处理中',
                        'TKSUCC0006'=>'退款成功',
                        'TKSUCC0007'=>'退款失败',
                        'TKSUCC0008'=>'通联审核不通过'
                    );
                    if (!array_key_exists($refundResult, $code)) {
                        $this->setErrors(array($refundResult, "退款未完成"));
                    } else {
                        $this->setErrors(array($refundResult, $code[$refundResult]));
                    }
                }
            } else {
                $this->setErrors(array(300, "签名验证失败"));
            }
        } else {
            $this->setErrors(array($err['ERRORCODE'], $err['ERRORMSG']));
        }
    }

    /**
     * @param $notify
     * @return array
     * POST
     */
    public function verifyNotifyAllinpay($notify)
    {
        if ($notify['merchantId'] != $this->allinpay_merchant_id) {
            $this->setErrors(array(403, "merchant id not match"));
            return array();
        }
        //$notify['signType']
        $to_sign = "";
        $to_sign = $to_sign . (!empty($notify['merchantId']) ? "merchantId=" . $notify['merchantId'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['version']) ? "version=" . $notify['version'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['language']) ? "language=" . $notify['language'] . "&" : "");
        $to_sign = $to_sign . ($notify['signType'] != "" ? "signType=" . $notify['signType'] . "&" : "");
        $to_sign = $to_sign . ($notify['payType'] != "" ? "payType=" . $notify['payType'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['issuerId']) ? "issuerId=" . $notify['issuerId'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['paymentOrderId']) ? "paymentOrderId=" . $notify['paymentOrderId'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['orderNo']) ? "orderNo=" . $notify['orderNo'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['orderDatetime']) ? "orderDatetime=" . $notify['orderDatetime'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['orderAmount']) ? "orderAmount=" . $notify['orderAmount'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['payDatetime']) ? "payDatetime=" . $notify['payDatetime'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['payAmount']) ? "payAmount=" . $notify['payAmount'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['ext1']) ? "ext1=" . $notify['ext1'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['ext2']) ? "ext2=" . $notify['ext2'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['payResult']) ? "payResult=" . $notify['payResult'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['errorCode']) ? "errorCode=" . $notify['errorCode'] . "&" : "");
        $to_sign = $to_sign . (!empty($notify['returnDatetime']) ? "returnDatetime=" . $notify['returnDatetime'] : "");
//        $to_sign = $to_sign . "&key=" . $this->allinpay_md5_key;

        if ($notify['signMsg'] == strtoupper(md5($to_sign . "&key=" . $this->allinpay_md5_key))) {
            if ($notify['payResult'] == "1") {
                $result = array();
                $result['order_sn'] = $notify['orderNo'];
                $result['amount'] = $notify['payAmount'] / 100;
                $result['raw'] = $notify;
                $result['custom'] = $notify['ext2'];
                $result['query_id'] = $notify['paymentOrderId'];
                return $result;
            } else {
                $this->setErrors(array(403, "pay not success"));
            }
        } else {
            $this->setErrors(array(403, "signature not match"));
        }
    }

    /**
     * 生成微信扫码模式一的url
     * @param $product_id
     * @param array $extra
     * @return string
     */
    public function buildQrCodeStringWx($product_id, $extra = array())
    {
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['nonce_str'] = isset($extra['nonce_str']) ? $extra['nonce_str'] : WxPayUtils::getNonceStr();
        $request['time_stamp'] = isset($extra['time_stamp']) ? $extra['time_stamp'] : time();
        $request['product_id'] = $product_id;
        $sign = WxPayUtils::MakeSign($request, $this->key);
        $result = 'weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s';
        return sprintf($result, $sign, $request['appid'], $request['mch_id'], $product_id, $request['time_stamp'], $request['nonce_str']);
    }

    public function refundUnion($params, $extra = array())
    {
        $result = false;
//        $url = 'https://101.231.204.80:5000/jiaofei/api/appTransReq.do';
        if ($this->debug) {
            $url = 'https://101.231.204.80:5000/gateway/api/appTransReq.do';
        } else {
            $url = 'https://gateway.95516.com/gateway/api/appTransReq.do';
        }

        $request = array();
        # 基本信息
        $request['version'] = '5.0.0'; # 版本号    version NS5 M   固定填写5.0.0   固定填写5.0.0
        $request['encoding'] = 'utf-8'; # 编码方式  encoding    ANS1..20    M   填写报文使用的字符编码，支持UTF-8与GBK编码   支持UTF-8、GBK
        $request['certId'] = UnionPayUtils::getSignCertId($this->cert_path, $this->cert_pwd); # 证书ID    certId  N1..128 M   填写签名私钥证书的Serial Number，该值可通过SDK获取   SDK代码从证书中读取
//        $request['encryptCertId'] = ''; # 加密证书ID  encryptCertId   N1..128 C   当需要加密敏感信息如CVN2、有效期、密码及其他账户信息如卡号、手机号时，填写加密公钥证书的Serial Number，该值可通过SDK获取
        $request['signMethod'] = '01'; # 签名方法   signMethod  N1..12  M   01：表示采用RSA  固定填写01
        $request['txnType'] = '04'; # 交易类型  txnType N2  M   取值：00：查询交易，01：消费，02：预授权，03：预授权完成，04：退货，05：圈存，11：代收，12：代付，13：账单支付，14：转账（保留），21：批量交易，22：批量查询，31：消费撤销，32：预授权撤销，33：预授权完成撤销，71：余额查询，72：实名认证-建立绑定关系，73：账单查询，74：解除绑定关系，75：查询绑定关系，77：发送短信验证码交易，78：开通查询交易，79：开通交易，94：IC卡脚本通知 固定填写01
        $request['txnSubType'] = '00'; # 交易子类   txnSubType  N2  M   依据实际交易类型填写。 固定填写01
        $request['bizType'] = '000201'; # 产品类型  bizType N6  M   取值：000101：基金业务之股票基金；000102：基金业务之货币基金；000201：B2C网关支付；000301：无跳转（商户侧）；000302：评级支付；000401：代付；000501：代收；000601：账单支付；000801：无跳转（机构侧）；000901：绑定支付；000902: Token支付；001001：订购；000202：B2B 除以上产品外其他接口默认送000000，对账文件下载接口必送000000   固定填写000201
        $request['channelType'] = '07';    # 渠道类型   channelType N2  M   05：语音 07：互联网 08：移动  固定填写08
        # 商户信息
        $request['accessType'] = '0'; # 接入类型    accessType  N1  M   0：商户直连接入1：收单机构接入    固定填写0
        $request['merId'] = $this->mer_Id; # 商户代码   merId   AN15    M   已被批准加入银联互联网系统的商户代码  如已签约，请使用真实商户号。如未签约，可以在本平台右上角点我的测试-测试参数获取测试环境商户号
        $request['backUrl'] = $params["notify_url"]; # 后台通知地址   backUrl ANS1..256   M   用于接收后台通知报文，必须上送完整的互联网可访问地址，支持HTTP与HTTPS协议（如：https://xxx.xxx.xxx....），地址中不能包含~   支持http和单向https，必须外网可以访问，例：https://xxx.xxx.c

        # 订单信息
        /* 商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则 */
        $request['orderId'] = $params["order_sn"];    # 商户订单号   orderId AN8..32 M   商户订单号，仅能用大小写字母与数字，不能用特殊字符   商户端生成，例：12345asdf
        $request['txnAmt'] = $params["refund_amount"] * 100;    # 交易金额  txnAmt  N1..12  M   单位为分，不能带小数点，样例：1元送100   整数，单位为分，例：1元填写100
        $request['txnTime'] = date('YmdHis'); # 订单发送时间  txnTime YYYYMMDDHHmmss  M   必须使用当前北京时间（年年年年月月日日时时分分秒秒）24小时制，样例：20151123152540，北京时间  取当前时间，例：20151118100505
        if (isset($extra['custom'])) {
            $request['reqReserved'] = $extra['custom']; # 请求方自定义域   reqReserved ANS1..1024  O   商户自定义保留域，交易应答时会原样返回 商户自定义保留域，交易应答时会原样返回
        }

        # 通知信息
        $request['origQryId'] = $params['query_id']; # 原交易查询流水号 origQryId   AN21    M   后续类交易（如退货、消费撤销等）送原交易的queryId    原始消费交易的queryId

        // 处理证书
        $request['signature'] = UnionPayUtils::sign($request, $this->cert_path, $this->cert_pwd); # 签名  signature   ANS1..1024  M   填写对报文摘要的签名，可通过SDK生成签名   SDK代码调用签名函数时自动计算

//        $response = array();
//        $response['respCode'];
//        $response['respMsg'];

        $opts = UnionPayUtils::createLinkString($request, false, true);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $opts);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
//        print_r($html);
//        var_dump($html);
        parse_str($html, $result);
//        var_dump($result);
        if ($result['respCode'] != '00') {
            $this->setErrors(array($result['respCode'], $result['respMsg']));
        } else {
            $result = true;
        }
        return $result;
    }

    /**
     * 即时到账有密退款接口
     *
     * @param $params
     * @param array $extra
     * @return boolean
     *        $params['notify_url'] = 'http://flat.test.wanthings.com/order/notify/alpay'; // 后台通知地址
     * $params['reason'] = ''; // 退款原因
     * $params['refund_amount'] = '0.01'; // 退款金额
     * $params['order_sn'] = '1111';// 订单编号
     * //        $extra['detail_data'] = '1111^0.01^测试付款';
     * $params['refund_sn'] = time(); // 退款单号
     */
    public function refundAlipayApi($params, $extra = array())
    {
        $url = 'https://openapi.alipay.com/gateway.do';

        $biz_content = array();
        $biz_content['out_trade_no'] = $params['order_sn'];
        $biz_content['refund_amount'] = $params['refund_amount'];
        if (isset($params['reason'])) {
            $biz_content['refund_reason'] = $params['reason'];
        }
        if (isset($params['refund_sn'])) {
            $biz_content['out_request_no'] = $params['refund_sn'];
        }

        $request = array();
        $request['app_id'] = $this->alipay_appid;
        $request['method'] = 'alipay.trade.refund';
        $request['format'] = 'JSON';
        $request['charset'] = 'utf-8';
        $request['sign_type'] = 'RSA';
        $request['timestamp'] = date('Y-m-d H:i:s');
        $request['version'] = '1.0';
        $request['biz_content'] = json_encode($biz_content);

        ksort($request);

        $stringToBeSigned = "";
        $i = 0;
        foreach ($request as $k => $v) {
            if ($i == 0) {
                $stringToBeSigned .= "$k" . "=" . "$v";
            } else {
                $stringToBeSigned .= "&" . "$k" . "=" . "$v";
            }
            $i++;
        }

        unset($k, $v);
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($stringToBeSigned, $sign, $res);
        openssl_free_key($res);
        $request['sign'] = base64_encode($sign);

        $resp = $this->request($url, $request);
        $response = json_decode($resp, true);
        if ($response['alipay_trade_refund_response']['code'] == '10000') {

            if ($response['alipay_trade_refund_response']['out_trade_no'] == $biz_content['out_trade_no'] && $response['alipay_trade_refund_response']['refund_fee'] == $biz_content['refund_amount']) {
                return true;
            } else {
                $this->setErrors(array($response['alipay_trade_refund_response']['code'], $response['alipay_trade_refund_response']['sub_msg']));
            }
        } else {
            $this->setErrors(array($response['alipay_trade_refund_response']['code'], $response['alipay_trade_refund_response']['sub_msg']));
        }
    }

    function request($url, $params = array(), $method = 'GET', $data_type = 'json')
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1);

        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        if (strtoupper($method) == 'GET') {
            $query_string = http_build_query($params);
            curl_setopt($ch, CURLOPT_URL, sprintf('%s?%s', $url, $query_string));
        } else {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_URL, $url);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            $errno = curl_errno($ch);
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errno, $errmsg));
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != '200') {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
        }
        curl_close($ch);
        if (true) {
//            var_dump($url);
//            var_dump($params);
//            var_dump($response);
            $notify = array();
            $notify['url'] = $url;
            $notify['params'] = $params;
            $notify['response'] = $response;
            file_put_contents(Yii::app()->getRuntimePath() . '/pay_request_' . date("Y_W") . '.log', sprintf("%s %s %s", date("Y-m-d H:i:s"), json_encode($notify), PHP_EOL), FILE_APPEND);
        }
        return $response;
    }

    /**
     * 即时到账有密退款接口
     *
     * @param $params
     * @param array $extra
     * @return array
     */
    public function refundAlipay($params, $extra = array())
    {
        $result = array();
        $result['url'] = "https://mapi.alipay.com/gateway.do?_input_charset=utf-8";

        $request = array();
        # 基本参数
        $request['service'] = 'refund_fastpay_by_platform_pwd';
        $request['partner'] = $this->partner;
        $request['_input_charset'] = 'utf-8';
        $request['notify_url'] = $params['notify_url'];

        # 业务参数
//        $request['seller_email'] = $this->sellId;
        $request['seller_user_id'] = $this->partner;
        $request['refund_date'] = date('Y-m-d H:i:s');
        $request['batch_no'] = date('Ymd') . $params['refund_sn']; // 跨天的BUG, 小几率

        if (!isset($extra['detail_data'])) {
            $request['detail_data'] = $params['order_sn'] . '^' . $params['refund_amount'] . '^' . $params['reason'];
        } else {
            $request['detail_data'] = $extra['detail_data'];
        }

        $request['batch_num'] = substr_count('|', $request['detail_data']) + 1;

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            $arg .= $key . "=" . $val . "&";
        }
        $arg = substr($arg, 0, count($arg) - 2);
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);
        $request['sign'] = $sign;
        $request['sign_type'] = 'RSA';

//        $request['sign'] = md5($arg . 'kzkjxozrps3g01ns2ncwtt15gpq47itl');
//        $request['sign_type'] = 'MD5';

        $result['method'] = 'get';
        $result['params'] = $request;
        return $result;
    }

    /**
     * 连连支付退款发起
     *
     * @param $params
     * @param array $extra
     */
    public function refundLlpay($params, $extra = array())
    {
        $result = array();
        $url = 'https://traderapi.lianlianpay.com/traderapi/refund.htm';
        $request = array();
        $request['no_refund'] = $params['refund_sn'];
        $request['dt_refund'] = date('YmdHis');
        $request['money_refund'] = $params['refund_amount'];
        $request['no_order'] = $params['order_sn'];
        if (isset($extra['query_id'])) {
            $request['oid_paybill'] = $params['query_id'];
        }
        if (isset($extra['order_date'])) {
            $request['dt_order'] = date('YmdHis', $extra['order_date']);
        }
        $request['notify_url'] = $params['notify_url'];
        $request['oid_partner'] = $this->llpay_merchant_id;
        $request['sign_type'] = 'MD5'; // RSA

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }
        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        $request['sign'] = md5($arg);
        $request_str = json_encode($request);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request_str);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($request_str))
        );
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
        $log = array();
        $log['url'] = $url;
        $log['request'] = $request;
        $log['respone'] = $html;
        file_put_contents(Yii::app()->getRuntimePath() . '/pay_debug_' . date("Y_W") . '.log', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), 'refundLlpay', json_encode($log), PHP_EOL), FILE_APPEND);
        $response = json_decode($html, true);

        if ($response['ret_code'] == '0000') {
            //check
            if ($response['sta_refund'] == '0' || $response['sta_refund'] == '1' || $response['sta_refund'] == '2') {
            } else {
                $this->setErrors(array(111, '退款失败'));
            }
        } else {
            $this->setErrors(array($response['ret_code'], $response['ret_msg']));
        }
    }

    /**
     * 支付宝批量有密付款功能
     *
     * @param $params
     * @param array $extra
     * @return array
     */
    public function transferAlipay($params, $extra = array())
    {
        $result = array();
        $url = "https://mapi.alipay.com/gateway.do";
        $result['url'] = "https://mapi.alipay.com/gateway.do?_input_charset=utf-8";

        $request = array();
        # 基本参数
        $request['service'] = 'batch_trans_notify';
        $request['partner'] = $this->partner;
        $request['_input_charset'] = 'utf-8';
        $request['notify_url'] = $params['notify_url'];
        # 业务参数
        $request['account_name'] = $this->partner;
        $request['detail_data'] = $params['detail_data'];
        $request['batch_no'] = $params['batch_no'];
        $request['batch_num'] = $params['batch_num'];
        $request['batch_fee'] = $params['batch_fee'];
        $request['email'] = $this->partner;
        $request['pay_date'] = isset($params['pay_date']) ? $params['pay_date'] : date('Ymd');
//        $request['buyer_account_name'];
//        $request['extend_param'];

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            $arg .= $key . "=" . $val . "&";
        }
        $arg = substr($arg, 0, count($arg) - 2);
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }
        $res = openssl_get_privatekey($this->primaryKey);
        openssl_sign($arg, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);
        $request['sign'] = $sign;
        $request['sign_type'] = 'RSA';

//        $request['sign'] = md5($arg . 'kzkjxozrps3g01ns2ncwtt15gpq47itl');
//        $request['sign_type'] = 'MD5';

        $result['method'] = 'get';
        $result['params'] = $request;
        return $result;
    }

    /**
     * 银联代付功能
     *
     * @param $params
     * @param array $extra
     * @return boolean
     */
    public function transferUnion($params, $extra = array())
    {
        $result = false;
//        $url = 'https://101.231.204.80:5000/jiaofei/api/appTransReq.do';
        if ($this->debug) {
            $url = 'https://101.231.204.80:5000/gateway/api/appTransReq.do';
        } else {
            $url = 'https://gateway.95516.com/gateway/api/appTransReq.do';
        }

//        $cert_path = '/home/lizf/Projects/flask-payment/acp_test_enc.cer';
//        $cert_path = '/home/lizf/Projects/flask-payment/unionpay/acp_prod_enc.cer';

        $request = array();
        # 基本信息
        $request['version'] = '5.0.0'; # 版本号    version NS5 M   固定填写5.0.0   固定填写5.0.0
        $request['encoding'] = 'utf-8'; # 编码方式  encoding    ANS1..20    M   填写报文使用的字符编码，支持UTF-8与GBK编码   支持UTF-8、GBK
        $request['certId'] = UnionPayUtils::getSignCertId($this->cert_path, $this->cert_pwd); # 证书ID    certId  N1..128 M   填写签名私钥证书的Serial Number，该值可通过SDK获取   SDK代码从证书中读取

//        $public_key = UnionPayUtils::getEncryptKey($cert_path);
        $public_key = UnionPayUtils::getEncryptKey($this->enc_cert_path);
        openssl_x509_read($public_key);
        $certdata = openssl_x509_parse($public_key);
        $request['encryptCertId'] = $certdata['serialNumber']; # 加密证书ID encryptCertId   N1..128 C   当需要加密敏感信息如CVN2、有效期、密码及其他账户信息如卡号、手机号时，填写加密公钥证书的Serial Number，该值可通过SDK获取
        $request['signMethod'] = '01'; # 签名方法   signMethod  N1..12  M   01：表示采用RSA  固定填写01
        $request['txnType'] = '12'; # 交易类型  txnType N2  M   取值：00：查询交易，01：消费，02：预授权，03：预授权完成，04：退货，05：圈存，11：代收，12：代付，13：账单支付，14：转账（保留），21：批量交易，22：批量查询，31：消费撤销，32：预授权撤销，33：预授权完成撤销，71：余额查询，72：实名认证-建立绑定关系，73：账单查询，74：解除绑定关系，75：查询绑定关系，77：发送短信验证码交易，78：开通查询交易，79：开通交易，94：IC卡脚本通知 固定填写01
        $request['txnSubType'] = '00'; # 交易子类   txnSubType  N2  M   依据实际交易类型填写。 固定填写01
        $request['bizType'] = '000401'; # 产品类型  bizType N6  M   取值：000101：基金业务之股票基金；000102：基金业务之货币基金；000201：B2C网关支付；000301：无跳转（商户侧）；000302：评级支付；000401：代付；000501：代收；000601：账单支付；000801：无跳转（机构侧）；000901：绑定支付；000902: Token支付；001001：订购；000202：B2B 除以上产品外其他接口默认送000000，对账文件下载接口必送000000   固定填写000201
        $request['channelType'] = '07';    # 渠道类型   channelType N2  M   05：语音 07：互联网 08：移动  固定填写08
        # 商户信息
        $request['accessType'] = '0'; # 接入类型    accessType  N1  M   0：商户直连接入1：收单机构接入    固定填写0
        $request['merId'] = $this->mer_Id; # 商户代码   merId   AN15    M   已被批准加入银联互联网系统的商户代码  如已签约，请使用真实商户号。如未签约，可以在本平台右上角点我的测试-测试参数获取测试环境商户号
        $request['backUrl'] = $params["notify_url"]; # 后台通知地址   backUrl ANS1..256   M   用于接收后台通知报文，必须上送完整的互联网可访问地址，支持HTTP与HTTPS协议（如：https://xxx.xxx.xxx....），地址中不能包含~   支持http和单向https，必须外网可以访问，例：https://xxx.xxx.c

        # 订单信息
        /* 商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则 */
        $request['orderId'] = $params["order_sn"];    # 商户订单号   orderId AN8..32 M   商户订单号，仅能用大小写字母与数字，不能用特殊字符   商户端生成，例：12345asdf
        $request['currencyCode'] = '156'; # 交易币种    currencyCode    AN3 M   币种格式必须为3位代码，境内客户取值：156（人民币） 固定156
        $request['txnAmt'] = $params["amount"] * 100;    # 交易金额 txnAmt  N1..12  M   单位为分，不能带小数点，样例：1元送100   整数，单位为分，例：1元填写100
        $request['txnTime'] = date('YmdHis', $params["ctime"]); # 订单发送时间    txnTime YYYYMMDDHHmmss  M   必须使用当前北京时间（年年年年月月日日时时分分秒秒）24小时制，样例：20151123152540，北京时间  取当前时间，例：20151118100505

        openssl_public_encrypt($params['bank_no'], $crypted, $public_key);
        $request['accNo'] = base64_encode($crypted); # 账号   accNo   AN1..1024   M   银行卡号。请求时使用加密公钥对交易账号加密，并做Base64编码后上送；应答时如需返回，则使用签名私钥进行解密。前台交易可由银联页面采集，也可由商户上送并返显，如需锁定返显卡号，应通过保留域（reserved）上送卡号锁定标识。  使用加密证书加密，并做base64编码
        $request['customerInfo'] = base64_encode("{" . UnionPayUtils::createLinkString(array('customerNm' => $params['real_name']), false, false) . "}"); # 6   银行卡验证信息及身份信息    customerInfo    ANS1..2048  C   该域需整体做Base64编码。所有子域需用“{}”包含，子域间以“&”符号链接。格式如下：{子域名1=值&子域名2=值&子域名3=值}借记卡可上送姓名、手机号、证件类型、证件号码；贷记卡可上送姓名、手机号、证件类型、证件号码、有效期、CVN2。前台交易可以由银联页面采集，无需商户上送，后台交易必须由商户上送。前台交易支持商户上送并返显的要素包含手机号，姓名，证件号。    后台交易必填，验证要素必须包含证件类型+证件号或者姓名中至少一个，代付交易customerInfo不上送手机

        if (isset($extra['custom'])) {
            $request['reqReserved'] = $extra['custom']; # 请求方自定义域   reqReserved ANS1..1024  O   商户自定义保留域，交易应答时会原样返回 商户自定义保留域，交易应答时会原样返回
        }

//        $request['reserved']; # 保留域   reserved    ANS1..2048  O   保留域包含多个子域，所有子域需用“{}”包含，子域间以“&”符号链接。 格式如下：{子域名1=值&子域名2=值&子域名3=值}。
//        $request['businessProductID'] = '00';
//        $request['accNo'] = '透传信息'; # 6   账号  accNo   AN1..512    C   银行卡号。请求时使用加密公钥对交易账号加密，并做Base64编码后上送；应答时如需返回，则使用签名私钥进行解密。前台交易可由银联页面采集，也可由商户上送并返显，如需锁定返显卡号，应通过保留域（reserved）上送卡号锁定标识。  业务运营中心开启了锁卡权限的情况下，送此字段可以指定用户在控件中输入的卡号。
//        $request['payerVerifiInfo'] = $params['product_name']; #10    付款方信息   payerVerifiInfo ANS64   C   包含多个子域，所有子域需用“{}”包含，子域间以“&”符号链接 格式如下：{子域名1=值&子域名2=值} 各子域取说明请点击查看

        // 处理证书
        $request['signature'] = UnionPayUtils::sign($request, $this->cert_path, $this->cert_pwd); # 签名  signature   ANS1..1024  M   填写对报文摘要的签名，可通过SDK生成签名   SDK代码调用签名函数时自动计算

//        $response = array();
//        $response['respCode'];
//        $response['respMsg'];

//        var_dump($request);
        $opts = UnionPayUtils::createLinkString($request, false, true);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $opts);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
//        print_r($html);
//        var_dump($html);
        parse_str($html, $result);
        if (!empty($result)) {
//        var_dump($result_arr);
            if ($result['respCode'] != '00') {
                $this->setErrors(array($result['respCode'], $result['respMsg']));
            } else {
                $result = true;
            }
        } else {
            $this->setErrors(array(3000, $html));
        }
        return $result;
    }

    /**
     * 微信向openid付款
     *
     * @param $params
     * @param array $extra
     * @return array
     */
    public function transferWx($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

        /*
         公众账号appid  mch_appid   是   wx8888888888888888  String  微信分配的公众账号ID（企业号corpid即为此appId）
        商户号     mchid   是   1900000109  String(32)  微信支付分配的商户号
        设备号     device_info     否   013467007045764     String(32)  微信支付分配的终端设备号
        随机字符串   nonce_str   是   5K8264ILTKCH16CQ2502SI8ZNMTM67VS    String(32)  随机字符串，不长于32位
        签名  sign    是   C380BEC2BFD727A4B6845133519F3AD6    String(32)  签名，详见签名算法
        商户订单号   partner_trade_no    是   10000098201411111234567890  String  商户订单号，需保持唯一性
        用户openid    openid  是   oxTWIuGaIt6gTKsQRLau2M0yL16E    String  商户appid下，某用户的openid
        校验用户姓名选项    check_name  是   OPTION_CHECK    String  NO_CHECK：不校验真实姓名
        FORCE_CHECK：强校验真实姓名（未实名认证的用户会校验失败，无法转账）
        OPTION_CHECK：针对已实名认证的用户才校验真实姓名（未实名认证用户不校验，可以转账成功）
        收款用户姓名  re_user_name    可选  马花花     String  收款用户真实姓名。
        如果check_name设置为FORCE_CHECK或OPTION_CHECK，则必填用户真实姓名
        金额  amount  是   10099   int     企业付款金额，单位为分
        企业付款描述信息    desc    是   理赔  String  企业付款操作说明信息。必填。
        Ip地址    spbill_create_ip    是   192.168.0.1     String(32)  调用接口的机器Ip地址
         */

        $request = array();
        $request['appid'] = $this->appid;
        $request['mchid'] = $this->mch_id;
//        $request['device_info'];
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['partner_trade_no'] = $params['order_sn'];
        $request['openid'] = $params['target'];
        $request['check_name'] = 'OPTION_CHECK';
        if (isset($extra['real_name'])) {
            $request['re_user_name'] = $extra['real_name'];
        }
        $request['amount'] = $params['amount'] * 100;
        $request['desc'] = $params['title'];
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
//        $request['trade_type'] = 'APP';
//        $request['out_trade_no'] = $params['order_sn'];
//        $request['body'] = $params['product_name'];
//        $request['total_fee'] = $params['amount'] * 100;
//        $request['notify_url'] = $params['notify_url'];

//        if (isset($extra['product_intro'])) {
//            $request['detail'] = $extra['product_intro'];
//        }
//        if (isset($extra['custom'])) {
//            $request['attach'] = $extra['custom'];
//        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验

        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->apiclient_cert);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->apiclient_key);

        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // check$obj->CheckSign();
                if ($response['result_code'] == 'SUCCESS') {
                    $result['package'] = 'Sign=WXPay';
                    $result['appid'] = $response['appid'];
                    $result['partnerid'] = $response['mch_id'];
                    $result['prepayid'] = $response['prepay_id'];
                    $result['noncestr'] = $response['nonce_str'];
                    $result['timestamp'] = time();
                    $result['sign'] = WxPayUtils::MakeSign($result, $this->key);
                    // 冗余数据
                    $result['nonce_str'] = $response['nonce_str'];
                    $result['package_val'] = 'Sign=WXPay';
                    $result['prepay_id'] = $response['prepay_id'];
                    $result['partner_id'] = $response['mch_id'];
                    $result['app_id'] = $response['appid'];
                    return $result;
                } else {
                    $this->setErrors(array(301, $response['err_code_des']));
                }
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    /**
     * 微信支付退款
     *
     * @param $params
     * @param array $extra
     * @return array
     */
    public function refundWx($params, $extra = array())
    {
        $result = array();
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';

        /*
公众账号ID  appid   是   String(32)  wx8888888888888888  微信分配的公众账号ID（企业号corpid即为此appId）
商户号     mch_id  是   String(32)  1900000109  微信支付分配的商户号
设备号     device_info     否   String(32)  013467007045764     终端设备号
随机字符串   nonce_str   是   String(32)  5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串，不长于32位。推荐随机数生成算法
签名  sign    是   String(32)  C380BEC2BFD727A4B6845133519F3AD6    签名，详见签名生成算法
微信订单号   transaction_id  二选一     String(28)  1217752501201407033233368018    微信生成的订单号，在支付通知中有返回
商户订单号   out_trade_no    String(32)  1217752501201407033233368018    商户侧传给微信的订单号
商户退款单号  out_refund_no   是   String(32)  1217752501201407033233368018    商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔
订单金额    total_fee   是   Int     100     订单总金额，单位为分，只能为整数，详见支付金额
退款金额    refund_fee  是   Int     100     退款总金额，订单总金额，单位为分，只能为整数，详见支付金额
货币种类    refund_fee_type     否   String(8)   CNY     货币类型，符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
操作员     op_user_id  是   String(32)  1900000109  操作员帐号, 默认为商户号
退款资金来源  refund_account  否   String(30)  REFUND_SOURCE_RECHARGE_FUNDS

仅针对老资金流商户使用

REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款（默认使用未结算资金退款）

REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
         */

        $request = false;
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
//        $request['device_info'];
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['out_trade_no'] = $params['order_sn'];
        $request['out_refund_no'] = $params['refund_sn'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['refund_fee'] = $params['refund_amount'] * 100;
        $request['refund_fee_type'] = 'CNY';
        $request['op_user_id'] = $this->mch_id;
        $request['refund_account'] = 'REFUND_SOURCE_RECHARGE_FUNDS';
//        $request['refund_account'] = 'REFUND_SOURCE_UNSETTLED_FUNDS';
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验

        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->apiclient_cert);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->apiclient_key);

        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // check$obj->CheckSign();
                if ($response['result_code'] == 'SUCCESS') {
                    $result = true;
                } else {
                    $this->setErrors(array(301, $response['err_code_des']));
                }
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    public function buildPayInfoSwift($params, $extra = array())
    {
        if (empty($this->swiftpass_mch_id) || empty($this->swiftpass_key)) {
            $this->setErrors(array(501, '没有配置商户参数: swift'));
            return false;
        }

        $result = array();
        $url = "https://pay.swiftpass.cn/pay/gateway";

        $request = array();
        $request['service'] = 'pay.weixin.jspay';
        $request['version'] = '2.0';
        $request['charset'] = 'UTF-8';

        $request['mch_id'] = $this->swiftpass_mch_id;
        if (!$this->debug) {
            // 正式环境才传此参数
            $request['sub_openid'] = $extra['openid'];
        }
        $request['sign_type'] = 'MD5';
        $request['is_raw'] = '0';
        $request['nonce_str'] = WxPayUtils::getNonceStr();

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        if (isset($params['return_url'])) {
            $request['callback_url'] = $params['return_url'];
        }
        $request['notify_url'] = $params['notify_url'];
        $request['mch_create_ip'] = $_SERVER['REMOTE_ADDR'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->swiftpass_key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['status'] != '0') {
                $this->setErrors(array(301, $response['message']));
            } else if ($response['result_code'] == '0') {
                $result['url'] = 'https://pay.swiftpass.cn/pay/jspay?token_id=' . $response['token_id'] . '&showwxtitle=1';
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    public function buildPayInfoSwiftNative($params, $extra = array())
    {
        if (empty($this->swiftpass_mch_id) || empty($this->swiftpass_key)) {
            $this->setErrors(array(501, '没有配置商户参数: swift_native'));
            return false;
        }

        $result = array();
        $url = "https://pay.swiftpass.cn/pay/gateway";

        $request = array();
        $request['service'] = 'pay.weixin.jspay';
        $request['version'] = '2.0';
        $request['charset'] = 'UTF-8';

        $request['mch_id'] = $this->swiftpass_mch_id;
        if (!$this->debug) {
            // 正式环境才传此参数
            $request['sub_openid'] = $extra['openid'];
        }
        $request['sign_type'] = 'MD5';
        $request['is_raw'] = '1';
        $request['nonce_str'] = WxPayUtils::getNonceStr();

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        if (isset($params['return_url'])) {
            $request['callback_url'] = $params['return_url'];
        }
        $request['notify_url'] = $params['notify_url'];
        $request['mch_create_ip'] = $_SERVER['REMOTE_ADDR'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        // TODO:
        $request['sign'] = WxPayUtils::MakeSign($request, $this->swiftpass_key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        $log = array();
        $log['request'] = $request;
        $log['respone'] = $data;
        $log['params'] = $params;
        $log['extra'] = $extra;
        file_put_contents(Yii::app()->getRuntimePath() . '/pay_debug_' . date("Y_W") . '.log', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), 'buildPayInfoSwiftNative', json_encode($log), PHP_EOL), FILE_APPEND);
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['status'] != '0') {
                $this->setErrors(array(301, $response['message']));
            } else if ($response['result_code'] == '0') {
                $result = json_decode($response['pay_info'], true);
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    // 微信 String(128), 银联不支持, 支付宝 String(128)
    // order_id 订单号
    // body 名称
    //* detail 可省略的详情
    //* attach 原版数据
    // amount 单位分
    //* time_expire 订单关闭时间, 最短失效时间间隔必须大于5分钟
    // notify_url
    // *limit_pay
    public function buildPayInfoWx($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['fee_type'] = 'CNY';
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $request['trade_type'] = 'APP';
//        $request['trade_type'] = 'NATIVE';
        $request['nonce_str'] = WxPayUtils::getNonceStr();
//        $request['product_id'] = '110';

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['notify_url'] = $params['notify_url'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                if ($response['result_code'] == 'SUCCESS') {
                    // check$obj->CheckSign();
                    $result['package'] = 'Sign=WXPay';
                    $result['appid'] = $response['appid'];
                    $result['partnerid'] = $response['mch_id'];
                    $result['prepayid'] = $response['prepay_id'];
                    $result['noncestr'] = $response['nonce_str'];
                    $result['timestamp'] = time();
                    $result['sign'] = WxPayUtils::MakeSign($result, $this->key);
                    // 冗余数据
                    $result['nonce_str'] = $response['nonce_str'];
                    $result['package_val'] = 'Sign=WXPay';
                    $result['prepay_id'] = $response['prepay_id'];
                    $result['partner_id'] = $response['mch_id'];
                    $result['app_id'] = $response['appid'];
                    return $result;
                } else {
                    $this->setErrors(array($response['err_code'], $response['err_code_des']));
                }
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    /**
     * 微信H5支付 (mweb_url)
     * @param $params
     * @param array $extra
     * @return array
     */
    public function buildPayInfoWxH5($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['fee_type'] = 'CNY';
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $request['trade_type'] = 'MWEB';
        $request['nonce_str'] = WxPayUtils::getNonceStr();
//        $request['product_id'] = $extra['product_id'];

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['notify_url'] = $params['notify_url'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // check$obj->CheckSign();
                $result['trade_type'] = $response['trade_type'];
                $result['prepay_id'] = $response['prepay_id'];
                $result['mweb_url'] = $response['mweb_url'];
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    /**
     * 微信扫码支付 模式二 (code_url)
     * @param $params
     * @param array $extra
     * @return array
     */
    public function buildPayInfoWxNativeMode2($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['fee_type'] = 'CNY';
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $request['trade_type'] = 'NATIVE';
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['product_id'] = $extra['product_id'];

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['notify_url'] = $params['notify_url'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // check$obj->CheckSign();
                $result['trade_type'] = $response['trade_type'];
                $result['prepay_id'] = $response['prepay_id'];
                $result['code_url'] = $response['code_url'];
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    /**
     * 微信扫码支付 模式一
     * @param $params
     * @param array $extra
     * @return array
     */
    public function buildPayInfoWxNativeMode1($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['fee_type'] = 'CNY';
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $request['trade_type'] = 'NATIVE';
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['product_id'] = $extra['product_id'];

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['notify_url'] = $params['notify_url'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // 组织唤起支付用的xml
                $resp = array();
                $resp['return_code'] = 'SUCCESS';
                $resp['return_msg'] = 'OK';
                $resp['appid'] = $this->appid;
                $resp['mch_id'] = $this->mch_id;
                $resp['nonce_str'] = WxPayUtils::getNonceStr();;
                $resp['result_code'] = 'SUCCESS';
                $resp['prepay_id'] = $response['prepay_id'];
                $resp['sign'] = WxPayUtils::MakeSign($resp, $this->key);

                $result['appId'] = $response['appid'];
                $result['signType'] = 'MD5';
                $result['package'] = 'prepay_id=' . $response['prepay_id'];
                $result['nonceStr'] = $response['nonce_str'];
                $result['timeStamp'] = "" . time();
                $result['paySign'] = WxPayUtils::MakeSign($result, $this->key);
                // 冗余数据
                $result['xml'] = WxPayUtils::ToXml($resp);
//                $result['prepay_id'] = $response['prepay_id'];
//                $result['code_url'] = $response['code_url'];
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    public function buildPayInfoWxJsapi($params, $extra = array())
    {
        $result = array();
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        $request = array();
        $request['appid'] = $this->appid;
        $request['mch_id'] = $this->mch_id;
        $request['fee_type'] = 'CNY';
        $request['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $request['trade_type'] = 'JSAPI';
        $request['nonce_str'] = WxPayUtils::getNonceStr();
        $request['openid'] = $extra['openid'];

        $request['out_trade_no'] = $params['order_sn'];
        $request['body'] = $params['product_name'];
        $request['total_fee'] = $params['amount'] * 100;
        $request['notify_url'] = $params['notify_url'];

        if (isset($extra['product_intro'])) {
            $request['detail'] = $extra['product_intro'];
        }
        if (isset($extra['custom'])) {
            $request['attach'] = $extra['custom'];
        }
//        var_dump($request);
        $request['sign'] = WxPayUtils::MakeSign($request, $this->key);
//        var_dump($request);

        $xml = WxPayUtils::ToXml($request);

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            $response = WxPayUtils::FromXml($data);
//            var_dump($data);
//            var_dump($response);
            if ($response['return_code'] == 'FAIL') {
                $this->setErrors(array(301, $response['return_msg']));
            } else if ($response['return_code'] == 'SUCCESS') {
                // check$obj->CheckSign();
                $result['appId'] = $response['appid'];
                $result['signType'] = 'MD5';
                $result['package'] = 'prepay_id=' . $response['prepay_id'];
                $result['nonceStr'] = $response['nonce_str'];
                $result['timeStamp'] = "" . time();
                $result['paySign'] = WxPayUtils::MakeSign($result, $this->key);
                return $result;
            }
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->setErrors(array($error, "curl出错，错误码: $error"));
        }
        return $result;
    }

    public function buildPayInfoUnion($params, $extra = array())
    {
        $result = array();
//        $url = 'https://101.231.204.80:5000/jiaofei/api/appTransReq.do';
        if ($this->debug) {
            $url = 'https://101.231.204.80:5000/gateway/api/appTransReq.do';
        } else {
            $url = 'https://gateway.95516.com/gateway/api/appTransReq.do';
        }

        $request = array();
        # 基本信息
        $request['version'] = '5.0.0'; # 版本号    version NS5 M   固定填写5.0.0   固定填写5.0.0
        $request['encoding'] = 'utf-8'; # 编码方式  encoding    ANS1..20    M   填写报文使用的字符编码，支持UTF-8与GBK编码   支持UTF-8、GBK
        $request['certId'] = UnionPayUtils::getSignCertId($this->cert_path, $this->cert_pwd); # 证书ID    certId  N1..128 M   填写签名私钥证书的Serial Number，该值可通过SDK获取   SDK代码从证书中读取
        $request['signMethod'] = '01'; # 签名方法   signMethod  N1..12  M   01：表示采用RSA  固定填写01
        $request['txnType'] = '01'; # 交易类型  txnType N2  M   取值：00：查询交易，01：消费，02：预授权，03：预授权完成，04：退货，05：圈存，11：代收，12：代付，13：账单支付，14：转账（保留），21：批量交易，22：批量查询，31：消费撤销，32：预授权撤销，33：预授权完成撤销，71：余额查询，72：实名认证-建立绑定关系，73：账单查询，74：解除绑定关系，75：查询绑定关系，77：发送短信验证码交易，78：开通查询交易，79：开通交易，94：IC卡脚本通知 固定填写01
        $request['txnSubType'] = '01'; # 交易子类   txnSubType  N2  M   依据实际交易类型填写。 固定填写01
        $request['bizType'] = '000201'; # 产品类型  bizType N6  M   取值：000101：基金业务之股票基金；000102：基金业务之货币基金；000201：B2C网关支付；000301：无跳转（商户侧）；000302：评级支付；000401：代付；000501：代收；000601：账单支付；000801：无跳转（机构侧）；000901：绑定支付；000902: Token支付；001001：订购；000202：B2B 除以上产品外其他接口默认送000000，对账文件下载接口必送000000   固定填写000201
        $request['channelType'] = '08';    # 渠道类型   channelType N2  M   05：语音 07：互联网 08：移动  固定填写08
        # 商户信息
        $request['accessType'] = '0'; # 接入类型    accessType  N1  M   0：商户直连接入1：收单机构接入    固定填写0
        $request['merId'] = $this->mer_Id; # 商户代码   merId   AN15    M   已被批准加入银联互联网系统的商户代码  如已签约，请使用真实商户号。如未签约，可以在本平台右上角点我的测试-测试参数获取测试环境商户号
        $request['backUrl'] = $params["notify_url"]; # 后台通知地址   backUrl ANS1..256   M   用于接收后台通知报文，必须上送完整的互联网可访问地址，支持HTTP与HTTPS协议（如：https://xxx.xxx.xxx....），地址中不能包含~   支持http和单向https，必须外网可以访问，例：https://xxx.xxx.c

        # $request['frontUrl'] = $_POST["frontUrl"];  #前台通知地址
        # 订单信息
        /* 商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则 */
        $request['orderId'] = $params["order_sn"];    # 商户订单号   orderId AN8..32 M   商户订单号，仅能用大小写字母与数字，不能用特殊字符   商户端生成，例：12345asdf
        $request['currencyCode'] = '156'; # 交易币种    currencyCode    AN3 M   币种格式必须为3位代码，境内客户取值：156（人民币） 固定156
        $request['txnAmt'] = $params["amount"] * 100;    # 交易金额 txnAmt  N1..12  M   单位为分，不能带小数点，样例：1元送100   整数，单位为分，例：1元填写100
        $request['txnTime'] = $params["ctime"]; # 订单发送时间    txnTime YYYYMMDDHHmmss  M   必须使用当前北京时间（年年年年月月日日时时分分秒秒）24小时制，样例：20151123152540，北京时间  取当前时间，例：20151118100505
        if (isset($extra['custom'])) {
            $request['reqReserved'] = $extra['custom']; # 请求方自定义域   reqReserved ANS1..1024  O   商户自定义保留域，交易应答时会原样返回 商户自定义保留域，交易应答时会原样返回
        }
        if (isset($extra['timeout'])) {
            $request['payTimeout'] = $extra['timeout']; # 5 支付超时时间  payTimeout  YYYYMMDDHHmmss  O   超过此时间后，除网银交易外，其他交易银联系统会拒绝受理，提示超时。 跳转银行网银交易如果超时后交易成功，会自动退款，大约5个工作日金额返还到持卡人账户。 此时间建议取支付时的北京时间加15分钟。   订单支付超时时间，例：20151118101505。超过此时间未完成支付时银联页面会提示超时，不允许后续支付。
        }
//        $request['accNo'] = '透传信息'; # 6   账号  accNo   AN1..512    C   银行卡号。请求时使用加密公钥对交易账号加密，并做Base64编码后上送；应答时如需返回，则使用签名私钥进行解密。前台交易可由银联页面采集，也可由商户上送并返显，如需锁定返显卡号，应通过保留域（reserved）上送卡号锁定标识。  业务运营中心开启了锁卡权限的情况下，送此字段可以指定用户在控件中输入的卡号。
        $request['orderDesc'] = $params['product_name']; # # 8  订单描述    orderDesc   ANS1..32    C   描述订单信息，显示在银联支付控件或客户端支付界面中   上送时可在控件内显示该信息，但仅用于控件显示，不会在商户和用户的对账单中出现。

        // 处理证书
        $request['signature'] = UnionPayUtils::sign($request, $this->cert_path, $this->cert_pwd); # 签名  signature   ANS1..1024  M   填写对报文摘要的签名，可通过SDK生成签名   SDK代码调用签名函数时自动计算

//        $response = array();
//        $response['respCode'];
//        $response['respMsg'];

        $opts = UnionPayUtils::createLinkString($request, false, true);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $opts);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
//        print_r($html);
        parse_str($html, $result);
//        var_dump($result_arr);
        if ($result['respCode'] != '00') {
            $this->setErrors(array($result['respCode'], $result['respMsg']));
        }
        return $result;
    }

    public function buildPayInfoUnionWeb($params, $extra = array())
    {
        $result = array();
        if ($this->debug) {
            $result['url'] = 'https://101.231.204.80:5000/gateway/api/frontTransReq.do';
        } else {
            $result['url'] = 'https://gateway.95516.com/gateway/api/frontTransReq.do';
        }

        $request = array();
        # 基本信息
        $request['version'] = '5.0.0'; # 版本号    version NS5 M   固定填写5.0.0   固定填写5.0.0
        $request['encoding'] = 'utf-8'; # 编码方式  encoding    ANS1..20    M   填写报文使用的字符编码，支持UTF-8与GBK编码   支持UTF-8、GBK
        $request['certId'] = UnionPayUtils::getSignCertId($this->cert_path, $this->cert_pwd); # 证书ID    certId  N1..128 M   填写签名私钥证书的Serial Number，该值可通过SDK获取   SDK代码从证书中读取
        $request['signMethod'] = '01'; # 签名方法   signMethod  N1..12  M   01：表示采用RSA  固定填写01
        $request['txnType'] = '01'; # 交易类型  txnType N2  M   取值：00：查询交易，01：消费，02：预授权，03：预授权完成，04：退货，05：圈存，11：代收，12：代付，13：账单支付，14：转账（保留），21：批量交易，22：批量查询，31：消费撤销，32：预授权撤销，33：预授权完成撤销，71：余额查询，72：实名认证-建立绑定关系，73：账单查询，74：解除绑定关系，75：查询绑定关系，77：发送短信验证码交易，78：开通查询交易，79：开通交易，94：IC卡脚本通知 固定填写01
        $request['txnSubType'] = '01'; # 交易子类   txnSubType  N2  M   依据实际交易类型填写。 固定填写01
        $request['bizType'] = '000201'; # 产品类型  bizType N6  M   取值：000101：基金业务之股票基金；000102：基金业务之货币基金；000201：B2C网关支付；000301：无跳转（商户侧）；000302：评级支付；000401：代付；000501：代收；000601：账单支付；000801：无跳转（机构侧）；000901：绑定支付；000902: Token支付；001001：订购；000202：B2B 除以上产品外其他接口默认送000000，对账文件下载接口必送000000   固定填写000201
        $request['channelType'] = '07';    # 渠道类型   channelType N2  M   05：语音 07：互联网 08：移动  固定填写08
        # 商户信息
        $request['accessType'] = '0'; # 接入类型    accessType  N1  M   0：商户直连接入1：收单机构接入    固定填写0
        $request['merId'] = $this->mer_Id; # 商户代码   merId   AN15    M   已被批准加入银联互联网系统的商户代码  如已签约，请使用真实商户号。如未签约，可以在本平台右上角点我的测试-测试参数获取测试环境商户号
        $request['backUrl'] = $params["notify_url"]; # 后台通知地址   backUrl ANS1..256   M   用于接收后台通知报文，必须上送完整的互联网可访问地址，支持HTTP与HTTPS协议（如：https://xxx.xxx.xxx....），地址中不能包含~   支持http和单向https，必须外网可以访问，例：https://xxx.xxx.c

        $request['frontUrl'] = isset($extra["return_url"]) ? $extra["return_url"] : $params["notify_url"];  #前台通知地址

        # 订单信息
        /* 商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则 */
        $request['orderId'] = $params["order_sn"];    # 商户订单号   orderId AN8..32 M   商户订单号，仅能用大小写字母与数字，不能用特殊字符   商户端生成，例：12345asdf
        $request['currencyCode'] = '156'; # 交易币种    currencyCode    AN3 M   币种格式必须为3位代码，境内客户取值：156（人民币） 固定156
        $request['txnAmt'] = $params["amount"] * 100;    # 交易金额 txnAmt  N1..12  M   单位为分，不能带小数点，样例：1元送100   整数，单位为分，例：1元填写100
        $request['txnTime'] = $params["ctime"]; # 订单发送时间    txnTime YYYYMMDDHHmmss  M   必须使用当前北京时间（年年年年月月日日时时分分秒秒）24小时制，样例：20151123152540，北京时间  取当前时间，例：20151118100505
        if (isset($extra['custom'])) {
            $request['reqReserved'] = $extra['custom']; # 请求方自定义域   reqReserved ANS1..1024  O   商户自定义保留域，交易应答时会原样返回 商户自定义保留域，交易应答时会原样返回
        }
        if (isset($extra['timeout'])) {
            $request['payTimeout'] = $extra['timeout']; # 5 支付超时时间  payTimeout  YYYYMMDDHHmmss  O   超过此时间后，除网银交易外，其他交易银联系统会拒绝受理，提示超时。 跳转银行网银交易如果超时后交易成功，会自动退款，大约5个工作日金额返还到持卡人账户。 此时间建议取支付时的北京时间加15分钟。   订单支付超时时间，例：20151118101505。超过此时间未完成支付时银联页面会提示超时，不允许后续支付。
        }
        $request['orderDesc'] = $params['product_name']; # # 8  订单描述    orderDesc   ANS1..32    C   描述订单信息，显示在银联支付控件或客户端支付界面中   上送时可在控件内显示该信息，但仅用于控件显示，不会在商户和用户的对账单中出现。

        // 处理证书
        $request['signature'] = UnionPayUtils::sign($request, $this->cert_path, $this->cert_pwd); # 签名  signature   ANS1..1024  M   填写对报文摘要的签名，可通过SDK生成签名   SDK代码调用签名函数时自动计算

        $result['params'] = $request;
        return $result;
    }

    /**
     * 连连支付退款结果查询
     *
     * @param $params
     * @return array|bool
     */
    public function refundQueryLlpay($params)
    {
        $result = array();
        $request = array();
        $request['oid_partner'] = $this->llpay_merchant_id;
        $request['sign_type'] = 'MD5';
        $request['no_refund'] = $params['refund_sn'];
        $request['dt_refund'] = date('YmdHis');

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }
        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        $request['sign'] = md5($arg);
        $req = json_encode($request);

        $ch = curl_init();
        if ($this->debug) {
            $url = 'https://test.yintong.com.cn/queryapi/orderquery.htm';
        } else {
            $url = 'https://queryapi.lianlianpay.com/refundquery.htm';
        }
//        $url = 'http://127.0.0.1:5000/post';
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json;charset=utf-8',
            'Content-Length: ' . strlen($req)
        ));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
        $resp = curl_exec($ch);
//        var_dump($resp);

        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        curl_close($ch);
        $ret_json = json_decode($resp, true);
        if ($ret_json['ret_code'] != '0000') {
            $this->setErrors(array($ret_json['ret_code'], $ret_json['ret_msg']));
            return $result;
        } else if ($ret_json['sta_refund'] == '2') {
            // 基本参数
            $ret_json['ret_code'];
            $ret_json['ret_msg'];
            $ret_json['sign_type'];
            $ret_json['sign'];
            // TODO
            // 业务参数
            $ret_json['oid_partner'];
            $result['refund_sn'] = $ret_json['no_refund'];
            $ret_json['dt_refund'];
            $result['refund_amount'] = $ret_json['money_refund'];
            $ret_json['oid_refundno'];
            $ret_json['sta_refund'];
            $ret_json['settle_date'];
            $result = true;
        } else {
            $this->setErrors(array($ret_json['ret_code'], $ret_json['ret_msg']));
            return $result;
        }
        return $result;
    }

    public function orderQueryLlpay($params)
    {
        $result = false;
        $request = array();
        $request['oid_partner'] = $this->llpay_merchant_id;
        $request['sign_type'] = 'MD5';
        $request['no_order'] = $params['order_sn'];
        $request['dt_order'] = date('YmdHis');
//        $request['oid_paybill'] = '';
        $request['query_version'] = '1.1'; // 可以1.0

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }
        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        $request['sign'] = md5($arg);
        $req = json_encode($request);

        $ch = curl_init();
        if ($this->debug) {
//            $url = 'https://test.yintong.com.cn/secure_server/x.htm';
            $url = 'https://test.yintong.com.cn/queryapi/orderquery.htm';
        } else {
//            $url = 'http://yintong.com.cn/secure_server/x.htm';
            $url = 'https://queryapi.lianlianpay.com/orderquery.htm';
        }
//        $url = 'http://127.0.0.1:5000/post';
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json;charset=utf-8',
            'Content-Length: ' . strlen($req)
        ));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
        $resp = curl_exec($ch);
//        var_dump($resp);

        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return $result;
        }
        $ret_json = json_decode($resp, true);
        if ($ret_json['ret_code'] != '0000') {
            $this->setErrors(array($ret_json['ret_code'], $ret_json['ret_msg']));
            return $result;
        } else if ($ret_json['result_pay'] == 'SUCCESS') {
            $result = true;
        } else {
            $this->setErrors(array($ret_json['ret_code'], $ret_json['result_pay']));
        }
        curl_close($ch);
        return $result;
        // 基本参数
        $ret['ret_code'];
        $ret['ret_msg'];
        $ret['sign_type'];
        $ret['sign'];
        // 业务参数
        $ret['result_pay'];
        $ret['oid_partner'];
        $ret['dt_order'];
        $ret['no_order'];
        $ret['oid_paybill'];
        $ret['money_order'];
        $ret['settle_date'];
        $ret['info_order'];
        $ret['pay_type'];
        $ret['bank_code'];
        $ret['bank_name'];// 不签名
        $ret['memo'];
        $ret['card_no'];// 不签名
    }

    public function orderQueryPayeco($params)
    {
        $result = false;
        $request = array();
        $request['Version'] = '2.0.0';
        $request['MerchantId'] = $this->payeco_merchant_id;
        $request['MerchOrderId'] = $params['order_sn'];
        $request['TradeTime'] = date('YmdHis');
        $request['Sign'] = PayecoUtils::sign($request, $this->payeco_cert_path, $this->payeco_cert_pwd);
        $request['TradeCode'] = 'QueryOrder';

        $args = '';
        foreach ($request as $k => $v) {
            $args .= $k . "=" . $v . "&";
        }
        $args = trim($args, "&");

        $ch = curl_init();
        if ($this->debug) {
            $url = $this->payeco_test_url . '/ppi/merchant/itf.do?' . $args;
        } else {
            $url = $this->payeco_url . '/ppi/merchant/itf.do?' . $args;
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        curl_close($ch);

        libxml_disable_entity_loader(true);
        $ret = json_decode(json_encode(simplexml_load_string($html, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        if ($ret['head']['retCode'] == '0000') {
//            var_dump($ret);
            if ($ret['body']['Status'] == '02') {
                $result = true;
            }
        } else {
            $error = array($ret['head']['retCode'], $ret['head']['retMsg']);
            $this->setErrors($error);
        }
        return $result;
    }

    /**
     * 构建易联支付Web版本支付参数
     * @param $params
     * @param array $extra
     * @return array
     */
    public function buildPayInfoPayecoWeb($params, $extra = array())
    {
        $result = array();
        $result['method'] = 'post';
        $result['params'] = array();
        if ($this->debug) {
            $result['url'] = $this->payeco_web_test_url;
        } else {
            $result['url'] = $this->payeco_web_url;
        }

        $ProcCode = '0200'; //消息类型
        $AccountNo = ''; //银行卡号
        $ProcessCode = '190011';  //处理
        $TransDatetime = date("YmdHis", time()); //传输日期时间
        $AcqSsn = date("mdH"); //系统跟踪号
        $TransData = ''; //其他业务资料，用“|”分隔，例如：银行代码
        $Reference = isset($extra['custom']) ? $extra['custom'] : ''; //系统参考号  原值返回
        $TerminalNo = $this->payeco_web_merchant_tid; //终端号
        $MerchantNo = $this->payeco_web_merchant_id; //商户号

        $Amount = $params['amount'];
        $Description = $params['product_name'];
        $SynAddress = $params['return_url'];
        $AsynAddress = $params['notify_url'];
        $remark = $params['product_name'];
        $MerchantOrderNo = $params['order_sn'];
        $MerchantName = isset($params['merchant_name']) ? $params['merchant_name'] : '网信天成';

        // 组建令牌
        $macClear = '';
        $macClear .= !empty($ProcCode) ? $ProcCode . ' ' : '';
        $macClear .= !empty($AccountNo) ? $AccountNo . ' ' : '';
        $macClear .= !empty($ProcessCode) ? $ProcessCode . ' ' : '';
        $macClear .= !empty($Amount) ? $Amount . ' ' : '';
        $macClear .= !empty($TransDatetime) ? $TransDatetime . ' ' : '';
        $macClear .= !empty($AcqSsn) ? $AcqSsn . ' ' : '';
        $macClear .= !empty($OrderNo) ? $OrderNo . ' ' : '';
        $macClear .= !empty($TransData) ? $TransData . ' ' : '';
        $macClear .= !empty($Reference) ? $Reference . ' ' : '';
        $macClear .= !empty($TerminalNo) ? $TerminalNo . ' ' : '';
        $macClear .= !empty($MerchantNo) ? $MerchantNo . ' ' : '';
        $macClear .= !empty($MerchantOrderNo) ? $MerchantOrderNo . ' ' : '';
        $macClear = trim($macClear);
        $mac = md5(strtoupper($macClear) . " " . $this->payeco_web_merchant_key);
        $mac = strtoupper($mac);

        // 组建xml
        $xml = '<?xml version="1.0" encoding="UTF-8"?>';
        $xml .= '<x:NetworkRequest xmlns:x="http://www.payeco.com" xmlns:xsi="http://www.w3.org">';
        $xml .= '<Version>2.0.0</Version>';
        $xml .= '<ProcCode>' . $ProcCode . '</ProcCode>';
        $xml .= '<ProcessCode>' . $ProcessCode . '</ProcessCode>';
        $xml .= '<AccountNo></AccountNo>';
        $xml .= '<AccountType></AccountType>';
        $xml .= '<MobileNo></MobileNo>';
        $xml .= '<Amount>' . $Amount . '</Amount>';
        $xml .= '<Currency>CNY</Currency>';
        $xml .= '<SynAddress>' . $SynAddress . '</SynAddress>';
        $xml .= '<AsynAddress>' . $AsynAddress . '</AsynAddress>';
        $xml .= '<Remark>' . $remark . '</Remark>';
        $xml .= '<TerminalNo>' . $TerminalNo . '</TerminalNo>';
        $xml .= '<MerchantNo>' . $MerchantNo . '</MerchantNo>';
        $xml .= '<MerchantOrderNo>' . $MerchantOrderNo . '</MerchantOrderNo>';
        $xml .= '<OrderNo></OrderNo>';
        $xml .= '<OrderFrom>12</OrderFrom>';
        $xml .= '<Language>00</Language>';
        $xml .= '<Description>' . $Description . '</Description>';
        $xml .= '<OrderType>00</OrderType>';
        $xml .= '<AcqSsn>' . $AcqSsn . '</AcqSsn>';
        $xml .= '<Reference></Reference>';
        $xml .= '<TransDatetime>' . $TransDatetime . '</TransDatetime>';
        $xml .= '<MerchantName>' . $MerchantName . '</MerchantName>';
        $xml .= '<TransData></TransData>';
        $xml .= '<IDCardName></IDCardName>';
        $xml .= '<IDCardNo></IDCardNo>';
        $xml .= '<BankAddress></BankAddress>';
        $xml .= '<IDCardType></IDCardType>';
        $xml .= '<BeneficiaryName></BeneficiaryName>';
        $xml .= '<BeneficiaryMobileNo></BeneficiaryMobileNo>';
        $xml .= '<DeliveryAddress></DeliveryAddress>';
        $xml .= '<IpAddress></IpAddress>';
        $xml .= '<Location></Location>';
        $xml .= '<UserFlag></UserFlag>';
        $xml .= '<MAC>' . $mac . '</MAC>';
        $xml .= '</x:NetworkRequest>';

        $result['params']['request_text'] = urlencode(base64_encode($xml));//最终请求格式

//        $error = array($result['head']['retCode'], $result['head']['retMsg']);
//        $this->setErrors($error);
//        print_r($result);
        return $result;
    }

    /**
     * 构建易联支付H5版本支付参数
     *
     * @param $params
     * @param array $extra
     * @return string|void
     */
    public function buildPayInfoPayecoH5($params, $extra = array())
    {
        $result = array();
        $result['method'] = 'get';
        $result['params'] = array();

        $request = array();
        $request['Version'] = '2.0.0';
        $request['MerchantId'] = $this->payeco_merchant_id;
        $request['MerchOrderId'] = $params['order_sn'];
        $request['Amount'] = $params['amount'];
        $request['OrderDesc'] = $params['product_name'];//字符最大50，中文最多16个
        $request['TradeTime'] = date('YmdHis');
        $request['ExpTime'] = '';//默认只有20分钟
        $request['NotifyUrl'] = $params['notify_url'];// urlencode
        $request['ReturnUrl'] = $params['return_url'];
        $request['ExtData'] = '';//字符最大128，中文最多40个
        $request['MiscData'] = '';
        $request['NotifyFlag'] = '0';
        $request['ClientIp'] = '';
        $request['Sign'] = PayecoUtils::sign($request, $this->payeco_cert_path, $this->payeco_cert_pwd);;
        $request['TradeCode'] = 'PayOrder';

        $args = '';
        foreach ($request as $k => $v) {
            if (in_array($k, array('OrderDesc', 'ExtData', 'MiscData'))) {
                $args .= $k . "=" . base64_encode($v) . "&";
            } elseif (in_array($k, array('NotifyUrl', 'ReturnUrl'))) {
                $args .= $k . "=" . urlencode($v) . "&";
            } else {
                $args .= $k . "=" . $v . "&";
            }
        }
        $args = trim($args, "&");

        $ch = curl_init();

        if ($this->debug) {
            $url = $this->payeco_test_url . '/ppi/merchant/itf.do?' . $args;
        } else {
            $url = $this->payeco_url . '/ppi/merchant/itf.do?' . $args;
        }
//        var_dump($url);
        curl_setopt($ch, CURLOPT_URL, $url);
//        var_dump($args);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        curl_close($ch);

        libxml_disable_entity_loader(true);
        $resp = json_decode(json_encode(simplexml_load_string($html, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        if ($resp['head']['retCode'] == '0000') {
            $args = sprintf("Version=%s&MerchantId=%s&MerchOrderId=%s&Amount=%s&TradeTime=%s&OrderId=%s&VerifyTime=%s&Sign=%s",
                $resp['body']['Version'], $resp['body']['MerchantId'], $resp['body']['MerchOrderId'],
                $resp['body']['Amount'], $resp['body']['TradeTime'], $resp['body']['OrderId'],
                $resp['body']['VerifyTime'], $resp['body']['Sign']
            );

            if ($this->debug) {
                $url = sprintf("%s/ppi/h5/plugin/itf.do?tradeId=h5Init&%s", $this->payeco_test_url, $args);
            } else {
                $url = sprintf("%s/ppi/h5/plugin/itf.do?tradeId=h5Init&%s", $this->payeco_url, $args);
            }

//            var_dump($url);
            $result['url'] = $url;
            return $result;
            // TOOD: log
//            return $result['body'];
        } else {
            $error = array($resp['head']['retCode'], $resp['head']['retMsg']);
            $this->setErrors($error);
//            var_dump($error);
        }
        return;
    }

    /**
     * 构建易联支付SDK版本支付参数
     *
     * @param $params
     * @param array $extra
     * @return string|void
     */
    public function buildPayInfoPayecoSDK($params, $extra = array())
    {
        $request = array();
        $request['Version'] = '2.0.0';
        $request['MerchantId'] = $this->payeco_merchant_id;
        $request['MerchOrderId'] = $params['order_sn'];
        $request['Amount'] = $params['amount'];
        $request['OrderDesc'] = $params['product_name'];//字符最大50，中文最多16个
        $request['TradeTime'] = date('YmdHis');
        $request['ExpTime'] = '';//默认只有20分钟
        $request['NotifyUrl'] = $params['notify_url'];// urlencode
        $request['ExtData'] = '';//字符最大128，中文最多40个
        $request['MiscData'] = '';
        $request['NotifyFlag'] = '0';
        $request['Sign'] = PayecoUtils::sign($request, $this->payeco_cert_path, $this->payeco_cert_pwd);;
        $request['TradeCode'] = 'PlaceOrder';

        $args = '';
        foreach ($request as $k => $v) {
            if (in_array($k, array('OrderDesc', 'ExtData', 'MiscData'))) {
                $args .= $k . "=" . base64_encode($v) . "&";
            } elseif (in_array($k, array('NotifyUrl'))) {
                $args .= $k . "=" . urlencode($v) . "&";
            } else {
                $args .= $k . "=" . $v . "&";
            }
        }
        $args = trim($args, "&");

        $ch = curl_init();
        if ($this->debug) {
            $url = $this->payeco_test_url . '/ppi/merchant/itf.do?' . $args;
        } else {
            $url = $this->payeco_url . '/ppi/merchant/itf.do?' . $args;
        }
//        var_dump($url);
        curl_setopt($ch, CURLOPT_URL, $url);
//        var_dump($args);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证HOST
        curl_setopt($ch, CURLOPT_SSLVERSION, 1); // http://php.net/manual/en/function.curl-setopt.php页面搜CURL_SSLVERSION_TLSv1
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type:application/x-www-form-urlencoded;charset=UTF-8'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($ch);
        if (curl_errno($ch)) {
            $errmsg = curl_error($ch);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != "200") {
            $errmsg = "http状态=" . curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            $this->setErrors(array($errmsg, "curl出错，错误码: $errmsg"));
            return;
        }
        curl_close($ch);

        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($html, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        if ($result['head']['retCode'] == '0000') {
//            var_dump($result);
//            $backSign = "Version=".$retVer."&MerchantId=".$retMerchantId."&MerchOrderId=".$retMerchOrderId
//                ."&Amount=".$retAmount."&TradeTime=".$retTradeTime."&OrderId=".$retOrderId;
//            //验证签名
//            $retSign = str_replace(" ", "+", $retSign);
//            unset($result['TransCode']);
            $json_result = array();
            $json_result['Version'] = $result['body']['Version'];
            $json_result['MerchantId'] = $result['body']['MerchantId'];
            $json_result['MerchOrderId'] = $result['body']['MerchOrderId'];
            $json_result['Amount'] = $result['body']['Amount'];
            $json_result['TradeTime'] = $result['body']['TradeTime'];
            $json_result['OrderId'] = $result['body']['OrderId'];
            $json_result['Sign'] = $result['body']['Sign'];
//            var_dump(json_encode($json_result));
            return json_encode($json_result);
            // TOOD: log
//            return $result['body'];
        } else {
            $error = array($result['head']['retCode'], $result['head']['retMsg']);
            $this->setErrors($error);
//            var_dump($error);
        }
        return;
    }

    /**
     * 构建连连支付SDK版本支付参数
     *
     * @param $params
     * @param array $extra
     * @return string|void
     */
    public function buildPayInfoLlpaySDK($params, $extra = array())
    {
        $request = array();
        // 业务参数
        $request['busi_partner'] = '109001';
//        $request['busi_partner'] = '101001';
        $request['no_order'] = $params['order_sn'];
        $request['dt_order'] = date('YmdHis');
        $request['name_goods'] = $params['product_name'];// 40
        $request['info_order'] = '2';// 255
        $request['money_order'] = $params['amount'];
        $request['notify_url'] = $params['notify_url'];// urlencode
//        $request['valid_order'] = '10080';// 默认7d
        // TODO :
//        $request['risk_item'] = '';
        $request['risk_item'] = '{"user_info_dt_register":"20131030122130"}';
        $request['risk_item'] = '{"user_info_bind_phone":"13958069593","user_info_dt_register":"201407251110120","frms_ware_category":"4.0","request_imei":"1122111221"}';

        // 基本参数
        $request['oid_partner'] = $this->llpay_merchant_id;
        $request['sign_type'] = 'MD5'; // RSA

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }
        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        $request['sign'] = md5($arg);

        // 以下字段不参加签名
//        $request['platform'] = '';
        $request['user_id'] = $extra['user_id'];
//        $request['force_bank'] = '';
//        $request['id_type'] = 0;
//        $request['id_no'] = '';
//        $request['acct_name'] = '';
//        $request['no_agree'] = '';
//        $request['shareing_data'] = '';

//        $req = json_encode($request);
//        print_r($request);
//        print_r($req);
        return $request;
    }

    /**
     * 构建连连支付WAP版本支付参数
     *
     * @param $params
     * @param array $extra
     * @return string|void
     */
    public function buildPayInfoLlpayWap($params, $extra = array())
    {
        $result = array();
        $result['method'] = 'post';
        $result['url'] = 'https://wap.lianlianpay.com/payment.htm';
//        $result['url'] = 'https://test.yintong.com.cn/llpayh5/payment.htm';

        $risk_item = array();
        $risk_item['user_info_mercht_userno'] = $extra['user_id'];
        if (isset($extra['frms_ware_category'])) {
            $risk_item['frms_ware_category'] = $extra['frms_ware_category'];
        }
        if (isset($extra['user_info_dt_register'])) {
            $risk_item['user_info_dt_register'] = $extra['user_info_dt_register'];
        }
        if (isset($extra['delivery_addr_province'])) {
            $risk_item['delivery_addr_province'] = $extra['delivery_addr_province'];
        }
        if (isset($extra['delivery_addr_city'])) {
            $risk_item['delivery_addr_city'] = $extra['delivery_addr_city'];
        }
        if (isset($extra['delivery_phone'])) {
            $risk_item['delivery_phone'] = $extra['delivery_phone'];
        }
        if (isset($extra['logistics_mode'])) {
            $risk_item['logistics_mode'] = $extra['logistics_mode'];
        }
        if (isset($extra['delivery_cycle'])) {
            $risk_item['delivery_cycle'] = $extra['delivery_cycle'];
        }

        $request = array();
        // 业务参数
        $request['busi_partner'] = '109001';
        $request['no_order'] = $params['order_sn'];
        $request['dt_order'] = date('YmdHis');
        $request['name_goods'] = $params['product_name'];// 40
        $request['info_order'] = '';// 255
        $request['money_order'] = $params['amount'];
        $request['notify_url'] = $params['notify_url'];// urlencode
        $request['url_return'] = $params['return_url'];// urlencode
//        $request['userreq_ip']; // *
//        $request['url_order']; // *
//        $request['valid_order'] = '10080';// * 默认7d
//        $request['bank_code']; // *
//        $request['pay_type']; // *
//        $request['no_agree']; // *
//        $request['shareing_data'] = ''; // *
        $request['risk_item'] = empty($risk_item) ? '' : json_encode($risk_item); // stripslashes
//        $request['id_type'] = ''; // *
//        $request['id_no'] = ''; // *
//        $request['acct_name'] = ''; // *
//        $request['flag_modify'] = ''; // *
//        $request['card_no'] = ''; // *
//        $request['back_url'] = ''; // *

        // 基本参数
        $request['version'] = '1.1';
        $request['app_request'] = '1'; // 1 Android    2 ios    3 WAP
        $request['oid_partner'] = $this->llpay_merchant_id;
//        $request['platform'] = ''; //*
        $request['user_id'] = $extra['user_id']; //*
        $request['timestamp'] = date('YmdHis'); //*
        $request['sign_type'] = 'MD5'; // RSA
        $request['syschnotify_flag'] = 1; // 0 点击通知 1 主动通知
//        $request['font_color'] = '';
//        $request['bg_color'] = '';

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }
        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        $request['sign'] = md5($arg);
        $result['params'] = array();
        $result['params']['req_data'] = json_encode($request);
        return $result;
    }

    /**
     * 构建连连支付SDK版本支付参数
     *
     * @param $params
     * @param array $extra
     * @return string|void
     */
    public function buildPayInfoLlpayWeb($params, $extra = array())
    {
        $result = array();
        $result['method'] = 'post';
//        if ($this->debug) {
//            $result['url'] = 'https://test.yintong.com.cn/payment/bankgateway.htm';
//        } else {
//            $result['url'] = 'https://yintong.com.cn/payment/bankgateway.htm';
//        }
        $result['url'] = 'https://cashier.lianlianpay.com/payment/bankgateway.htm';
//        $result['url'] = 'https://test.yintong.com.cn/payment/bankgateway.htm';
//        $result['url'] = 'https://cashier.lianlianpay.com/payment/bankgateway.htm';

        $risk_item = array();
        $risk_item['user_info_mercht_userno'] = $extra['user_id'];
        if (isset($extra['frms_ware_category'])) {
            $risk_item['frms_ware_category'] = $extra['frms_ware_category'];
        }
        if (isset($extra['user_info_dt_register'])) {
            $risk_item['user_info_dt_register'] = $extra['user_info_dt_register'];
        }
        if (isset($extra['delivery_addr_province'])) {
            $risk_item['delivery_addr_province'] = $extra['delivery_addr_province'];
        }
        if (isset($extra['delivery_addr_city'])) {
            $risk_item['delivery_addr_city'] = $extra['delivery_addr_city'];
        }
        if (isset($extra['delivery_phone'])) {
            $risk_item['delivery_phone'] = $extra['delivery_phone'];
        }
        if (isset($extra['logistics_mode'])) {
            $risk_item['logistics_mode'] = $extra['logistics_mode'];
        }
        if (isset($extra['delivery_cycle'])) {
            $risk_item['delivery_cycle'] = $extra['delivery_cycle'];
        }

        $request = array();
        // 业务参数
        $request['busi_partner'] = '109001';
//        $request['busi_partner'] = '101001';
        $request['no_order'] = $params['order_sn'];
        $request['dt_order'] = date('YmdHis');
        $request['name_goods'] = $params['product_name'];// 40
        $request['info_order'] = '';// 255
        $request['money_order'] = $params['amount'];
        $request['notify_url'] = $params['notify_url'];// urlencode
        $request['url_return'] = $params['return_url'];// urlencode
//        $request['userreq_ip']; // *
//        $request['url_order']; // *
//        $request['valid_order'] = '10080';// * 默认7d
//        $request['bank_code']; // *
//        $request['pay_type']; // *
//        $request['no_agree']; // *
//        $request['shareing_data'] = ''; // *
        $request['risk_item'] = empty($risk_item) ? '' : json_encode($risk_item); // stripslashes
//        $request['id_type'] = ''; // *
//        $request['id_no'] = ''; // *
//        $request['acct_name'] = ''; // *
//        $request['flag_modify'] = ''; // *
//        $request['card_no'] = ''; // *
//        $request['back_url'] = ''; // *

        // 基本参数
        $request['version'] = '1.0';
        $request['charset_name'] = 'UTF-8';
        $request['oid_partner'] = $this->llpay_merchant_id;
//        $request['platform'] = ''; //*
        $request['user_id'] = $extra['user_id']; //*
        $request['timestamp'] = date('YmdHis'); //*
        $request['sign_type'] = 'MD5'; // RSA

        ksort($request);
        reset($request);
        $arg = "";
        while (list ($key, $val) = each($request)) {
            if (!empty($val)) {
                $arg .= $key . "=" . $val . "&";
            }
        }

        // 追加md5签名key
        $arg .= 'key=' . $this->llpay_merchant_key;
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }
        $request['sign'] = md5($arg);

        $request['risk_item'] = stripslashes($request['risk_item']);

        $result['params'] = $request;
//        var_dump($result);
        return $result;
    }

    public function getErrors()
    {
        return $this->_errors;
    }

    public function resetErrors()
    {
        $this->_errors = array();
    }

    public function setErrors($error)
    {
        array_push($this->_errors, $error);
        $params = array();
        $params['server'] = $_SERVER;
        $params['post'] = $_POST;
        $params['get'] = $_GET;
        $params['request'] = $_REQUEST;
        $params['raw'] = file_get_contents("php://input");
        file_put_contents(Yii::app()->getRuntimePath() . '/pay_error_' . date("Y_W") . '.log', sprintf("%s %s %s %s", date("Y-m-d H:i:s"), json_encode($error), json_encode($params), PHP_EOL), FILE_APPEND);
    }

}

/**
 * AliPayUtils 支付宝工具类
 */
class AliPayUtils
{
    var $https_verify_url = 'https://mapi.alipay.com/gateway.do?service=notify_verify&';
    /**
     * HTTP形式消息验证地址
     */
    var $http_verify_url = 'http://notify.alipay.com/trade/notify_query.do?';

    /**
     * 针对notify_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyNotify()
    {
        if (empty($_POST)) {//判断POST来的数组是否为空
            return false;
        } else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_POST, $_POST["sign"]);
            //获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
            $responseTxt = 'true';
            if (!empty($_POST["notify_id"])) {
                $responseTxt = $this->getResponse($_POST["notify_id"]);
            }

            //写日志记录
            //if ($isSign) {
            //  $isSignStr = 'true';
            //}
            //else {
            //  $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n notify_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_POST);
            //logResult($log_text);

            //验证
            //$responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
            if (preg_match("/true$/i", $responseTxt) && $isSign) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 针对return_url验证消息是否是支付宝发出的合法消息
     * @return 验证结果
     */
    public function verifyReturn()
    {
        if (empty($_GET)) {//判断POST来的数组是否为空
            return false;
        } else {
            //生成签名结果
            $isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);
            //获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
            $responseTxt = 'true';
            if (!empty($_GET["notify_id"])) {
                $responseTxt = $this->getResponse($_GET["notify_id"]);
            }

            //写日志记录
            //if ($isSign) {
            //  $isSignStr = 'true';
            //}
            //else {
            //  $isSignStr = 'false';
            //}
            //$log_text = "responseTxt=".$responseTxt."\n return_url_log:isSign=".$isSignStr.",";
            //$log_text = $log_text.createLinkString($_GET);
            //logResult($log_text);

            //验证
            //$responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
            //isSign的结果不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
            if (preg_match("/true$/i", $responseTxt) && $isSign) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 获取返回时的签名验证结果
     * @param $para_temp 通知返回来的参数数组
     * @param $sign 返回的签名结果
     * @return 签名验证结果
     */
    public function getSignVeryfy($para_temp, $sign)
    {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = paraFilter($para_temp);

        //对待签名参数数组排序
        $para_sort = argSort($para_filter);

        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = createLinkstring($para_sort);

        $isSgin = false;
        switch (strtoupper(trim($this->alipay_config['sign_type']))) {
            case "RSA" :
                $isSgin = rsaVerify($prestr, trim($this->alipay_config['ali_public_key_path']), $sign);
                break;
            default :
                $isSgin = false;
        }

        return $isSgin;
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     * @param $notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    public static function getResponse($notify_id)
    {
        $transport = strtolower(trim($this->alipay_config['transport']));
        $partner = trim($this->alipay_config['partner']);
        $veryfy_url = '';
        if ($transport == 'https') {
            $veryfy_url = $this->https_verify_url;
        } else {
            $veryfy_url = $this->http_verify_url;
        }
        $veryfy_url = $veryfy_url . "partner=" . $partner . "&notify_id=" . $notify_id;
        $responseTxt = getHttpResponseGET($veryfy_url, $this->alipay_config['cacert']);

        return $responseTxt;
    }

    /**
     * RSA签名
     * @param $data 待签名数据
     * @param $private_key_path 商户私钥文件路径
     * return 签名结果
     */
    public static function rsaSign($data, $private_key_path)
    {
        $priKey = file_get_contents($private_key_path);
        $res = openssl_get_privatekey($priKey);
        openssl_sign($data, $sign, $res);
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * RSA验签
     * @param $data 待签名数据
     * @param $ali_public_key_path 支付宝的公钥文件路径
     * @param $sign 要校对的的签名结果
     * return 验证结果
     */
    public static function rsaVerify($data, $ali_public_key_path, $sign)
    {
        $pubKey = file_get_contents($ali_public_key_path);
        $res = openssl_get_publickey($pubKey);
        $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        openssl_free_key($res);
        return $result;
    }

    /**
     * RSA解密
     * @param $content 需要解密的内容，密文
     * @param $private_key_path 商户私钥文件路径
     * return 解密后内容，明文
     */
    public static function rsaDecrypt($content, $priKey)
    {
        $res = openssl_get_privatekey($priKey);
        //用base64将内容还原成二进制
        $content = base64_decode($content);
        //把需要解密的内容，按128位拆开解密
        $result = '';
        for ($i = 0; $i < strlen($content) / 128; $i++) {
            $data = substr($content, $i * 128, 128);
            openssl_private_decrypt($data, $decrypt, $res);
            $result .= $decrypt;
        }
        openssl_free_key($res);
        return $result;
    }

    /* *
     * 支付宝接口公用函数
     * 详细：该类是请求、通知返回两个文件所调用的公用函数核心处理文件
     * 版本：3.3
     * 日期：2012-07-19
     * 说明：
     * 以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
     * 该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
     */

    /**
     * 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    public static function createLinkstring($para)
    {
        $arg = "";
        while (list ($key, $val) = each($para)) {
            $arg .= $key . "=" . $val . "&";
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        return $arg;
    }

    /**
     * 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串，并对字符串做urlencode编码
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    public static function createLinkstringUrlencode($para)
    {
        $arg = "";
        while (list ($key, $val) = each($para)) {
            $arg .= $key . "=" . urlencode($val) . "&";
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, count($arg) - 2);

        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        return $arg;
    }

    /**
     * 除去数组中的空值和签名参数
     * @param $para 签名参数组
     * return 去掉空值与签名参数后的新签名参数组
     */
    public static function paraFilter($para)
    {
        $para_filter = array();
        while (list ($key, $val) = each($para)) {
            if ($key == "sign" || $key == "sign_type" || $val == "") {
                continue;
            } else {
                $para_filter[$key] = $para[$key];
            }
        }
        return $para_filter;
    }

    /**
     * 对数组排序
     * @param $para 排序前的数组
     * return 排序后的数组
     */
    public static function argSort($para)
    {
        ksort($para);
        reset($para);
        return $para;
    }

    /**
     * 写日志，方便测试（看网站需求，也可以改成把记录存入数据库）
     * 注意：服务器需要开通fopen配置
     * @param $word 要写入日志里的文本内容 默认值：空值
     */
    public static function logResult($word = '')
    {
        $fp = fopen("log.txt", "a");
        flock($fp, LOCK_EX);
        fwrite($fp, "执行日期：" . strftime("%Y%m%d%H%M%S", time()) . "\n" . $word . "\n");
        flock($fp, LOCK_UN);
        fclose($fp);
    }

    /**
     * 远程获取数据，POST模式
     * 注意：
     * 1.使用Crul需要修改服务器中php.ini文件的设置，找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效，目前默认路径是：getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * @param $para 请求的数据
     * @param $input_charset 编码格式。默认值：空值
     * return 远程输出的数据
     */
    public static function getHttpResponsePOST($url, $para, $input_charset = '')
    {
        if (trim($input_charset) != '') {
            $url = $url . "_input_charset=" . $input_charset;
        }
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_POST, true); // post传输数据
        curl_setopt($curl, CURLOPT_POSTFIELDS, $para);// post传输数据
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常，可打开此开关，以便查看异常内容
        curl_close($curl);

        return $responseText;
    }

    /**
     * 远程获取数据，GET模式
     * 注意：
     * 1.使用Crul需要修改服务器中php.ini文件的设置，找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效，目前默认路径是：getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * return 远程输出的数据
     */
    public static function getHttpResponseGET($url, $cacert_url)
    {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_CAINFO, $cacert_url);//证书地址
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常，可打开此开关，以便查看异常内容
        curl_close($curl);

        return $responseText;
    }

    /**
     * 实现多种字符编码方式
     * @param $input 需要编码的字符串
     * @param $_output_charset 输出的编码格式
     * @param $_input_charset 输入的编码格式
     * return 编码后的字符串
     */
    public static function charsetEncode($input, $_output_charset, $_input_charset)
    {
        $output = "";
        if (!isset($_output_charset)) $_output_charset = $_input_charset;
        if ($_input_charset == $_output_charset || $input == null) {
            $output = $input;
        } elseif (function_exists("mb_convert_encoding")) {
            $output = mb_convert_encoding($input, $_output_charset, $_input_charset);
        } elseif (function_exists("iconv")) {
            $output = iconv($_input_charset, $_output_charset, $input);
        } else die("sorry, you have no libs support for charset change.");
        return $output;
    }

    /**
     * 实现多种字符解码方式
     * @param $input 需要解码的字符串
     * @param $_output_charset 输出的解码格式
     * @param $_input_charset 输入的解码格式
     * return 解码后的字符串
     */
    public static function charsetDecode($input, $_input_charset, $_output_charset)
    {
        $output = "";
        if (!isset($_input_charset)) $_input_charset = $_input_charset;
        if ($_input_charset == $_output_charset || $input == null) {
            $output = $input;
        } elseif (function_exists("mb_convert_encoding")) {
            $output = mb_convert_encoding($input, $_output_charset, $_input_charset);
        } elseif (function_exists("iconv")) {
            $output = iconv($_input_charset, $_output_charset, $input);
        } else die("sorry, you have no libs support for charset changes.");
        return $output;
    }

    /**
     * 建立请求，以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
     * @param $para_temp 请求参数数组
     * @return 支付宝处理结果
     */
    public static function buildRequestHttp($para_temp)
    {
        $sResult = '';

        //待请求参数数组字符串
        $request_data = self::buildRequestPara($para_temp);

        //远程获取数据
        $sResult = getHttpResponsePOST($this->alipay_gateway_new, $this->alipay_config['cacert'], $request_data, trim(strtolower($this->alipay_config['input_charset'])));

        return $sResult;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param $para_temp 请求前的参数数组
     * @return 要请求的参数数组
     */
    public static function buildRequestPara($para_temp)
    {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = paraFilter($para_temp);

        //对待签名参数数组排序
        $para_sort = argSort($para_filter);

        //生成签名结果
        $mysign = $this->buildRequestMysign($para_sort);

        //签名结果与签名方式加入请求提交参数组中
        $para_sort['sign'] = $mysign;
        if ($para_sort['service'] != 'alipay.wap.trade.create.direct' && $para_sort['service'] != 'alipay.wap.auth.authAndExecute') {
            $para_sort['sign_type'] = strtoupper(trim($this->alipay_config['sign_type']));
        }

        return $para_sort;
    }

    /**
     * 生成签名结果
     * @param $para_sort 已排序要签名的数组
     * return 签名结果字符串
     */
    public static function buildRequestMysign($para_sort)
    {
        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = createLinkstring($para_sort);

        $mysign = "";
        switch (strtoupper(trim($this->alipay_config['sign_type']))) {
            case "MD5" :
                $mysign = md5Sign($prestr, $this->alipay_config['key']);
                break;
            case "RSA" :
            case "0001" :
                $mysign = rsaSign($prestr, $this->alipay_config['private_key_path']);
                break;
            default :
                $mysign = "";
        }
        return $mysign;
    }

    /**
     * 签名字符串
     * @param $prestr 需要签名的字符串
     * @param $key 私钥
     * return 签名结果
     */
    public static function md5Sign($prestr, $key)
    {
        $prestr = $prestr . $key;
        return md5($prestr);
    }

    /**
     * 验证签名
     * @param $prestr 需要签名的字符串
     * @param $sign 签名结果
     * @param $key 私钥
     * return 签名结果
     */
    public static function md5Verify($prestr, $sign, $key)
    {
        $prestr = $prestr . $key;
        $mysgin = md5($prestr);

        if ($mysgin == $sign) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 解析远程模拟提交后返回的信息
     * @param $str_text 要解析的字符串
     * @return 解析结果
     */
    public static function parseResponse($str_text, $priKey, $sign_type = '0001')
    {
        //以“&”字符切割字符串
        $para_split = explode('&', $str_text);
        //把切割后的字符串数组变成变量与数值组合的数组
        foreach ($para_split as $item) {
            //获得第一个=字符的位置
            $nPos = strpos($item, '=');
            //获得字符串长度
            $nLen = strlen($item);
            //获得变量名
            $key = substr($item, 0, $nPos);
            //获得数值
            $value = substr($item, $nPos + 1, $nLen - $nPos - 1);
            //放入数组中
            $para_text[$key] = $value;
        }

        if (!empty ($para_text['res_data'])) {
            //解析加密部分字符串
            if ($sign_type == '0001') {
                $para_text['res_data'] = self::rsaDecrypt($para_text['res_data'], $priKey);
            }

            //token从res_data中解析出来（也就是说res_data中已经包含token的内容）
            $doc = new DOMDocument();
            $doc->loadXML($para_text['res_data']);
            $para_text['request_token'] = $doc->getElementsByTagName("request_token")->item(0)->nodeValue;
        }

        return $para_text;
    }
}

/**
 * WxPayUtils 微信工具类
 */
class WxPayUtils
{
    public static function CheckSign($data, $key, $remote_sign = null)
    {
        $result = false;
        $local_sign = self::MakeSign($data, $key);
        if ($remote_sign == null) {
            $remote_sign = $data['sign'];
        }
        if ($local_sign == $remote_sign) {
            $result = true;
        }
        return $result;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public static function MakeSign($data, $key)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = self::ToUrlParams($data);
        //签名步骤二：在string后加入KEY
//        var_dump($string);
        $string = $string . "&key=" . $key;
//        var_dump($string);
        //签名步骤三：MD5加密
        $string = md5($string);
//        var_dump($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 格式化参数格式化成url参数
     */
    public static function ToUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
    public static function FromXml($xml)
    {
        if (!$xml) {
            throw new WxPayException("xml数据异常！");
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 输出xml字符
     * @throws WxPayException
     **/
    public static function ToXml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            throw new WxPayException("数组数据异常！");
        }

        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     *
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return 产生的随机字符串
     */
    public static function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
}

/**
 * 连连支付工具类
 */
class LlPayUtils
{

}

/**
 * 易联支付工具类
 */
class PayecoUtils
{
    const PLACE_ORDER = 1;
    const NOTIFY_ORDER = 2;
    const QUERY_ORDER = 3;

    public static function sign($params, $cert_path, $cert_pwd, $sign_type = PayecoUtils::PLACE_ORDER)
    {
        $args = '';
        foreach ($params as $k => $v) {
            $args .= $k . "=" . $v . "&";
        }

        $args = trim($args, "&");
//        var_dump($args);

        $pkcs12 = file_get_contents($cert_path);
        if (strrchr($cert_path, ".pem") != ".pem") {
            openssl_pkcs12_read($pkcs12, $certs, $cert_pwd);
            $pkey = openssl_get_privatekey($certs['pkey']);
        } else {
            $pkey = openssl_get_privatekey($pkcs12);
        }

//        openssl_sign($args, $sign, $pkey, 'md5WithRSAEncryption');
        openssl_sign($args, $sign, $pkey, OPENSSL_ALGO_MD5);
        openssl_free_key($pkey);
        $sign = base64_encode($sign);
        return $sign;
    }

    public static function verify($params, $sign, $pubKey)
    {
        $res = openssl_get_publickey($pubKey);
//        $result = (bool) openssl_verify($params, base64_decode($sign), $res, OPENSSL_ALGO_MD5);
        $result = (bool)openssl_verify($params, base64_decode($sign), $res, 'md5WithRSAEncryption');
        #print_r(openssl_pkey_get_details($res));
        openssl_free_key($res);
        return $result;
    }
}


/**
 * UnionPayUtils 银联工具类
 */
class UnionPayUtils
{
    public static function verify($params)
    {
//        global $log;
        // 公钥
        $public_key = self::getPulbicKeyByCertId($params['certId']);
//  echo $public_key.'<br/>';
        // 签名串
        $signature_str = $params['signature'];
        unset($params['signature']);
        $params_str = self::createLinkString($params, true, false);
//        $log->LogInfo('报文去[signature] key=val&串>' . $params_str);
        $signature = base64_decode($signature_str);
//  echo date('Y-m-d',time());
        $params_sha1x16 = sha1($params_str, false);
//        $log->LogInfo('摘要shax16>' . $params_sha1x16);
        $isSuccess = openssl_verify($params_sha1x16, $signature, $public_key, OPENSSL_ALGO_SHA1);
//        $log->LogInfo($isSuccess ? '验签成功' : '验签失败');
        return $isSuccess;
    }

    /**
     * 返回(签名)证书私钥 -
     *
     * @return unknown
     */
    public static function getPrivateKey($cert_path, $cert_pwd)
    {
        $pkcs12 = file_get_contents($cert_path);
        openssl_pkcs12_read($pkcs12, $certs, $cert_pwd);
        return $certs['pkey'];
    }

    /**
     * 讲数组转换为string
     *
     * @param $para 数组
     * @param $sort 是否需要排序
     * @param $encode 是否需要URL编码
     * @return string
     */
    public static function createLinkString($para, $sort, $encode)
    {
        if ($para == NULL || !is_array($para))
            return "";

        $linkString = "";
        if ($sort) {
            ksort($para);
            reset($para);
//            $para = argSort($para);
        }
        while (list ($key, $value) = each($para)) {
            if ($encode) {
                $value = urlencode($value);
            }
            $linkString .= $key . "=" . $value . "&";
        }
        // 去掉最后一个&字符
        $linkString = substr($linkString, 0, count($linkString) - 2);

        return $linkString;
    }

    public static function sign($params, $cert_path, $cert_pwd)
    {
//        $log->LogInfo('=====签名报文开始======');
//        var_dump($params);
        if (isset($params['signature'])) {
            unset($params['signature']);
        }
        // 转换成key=val&串
        $params_str = self::createLinkString($params, true, false);
//        $log->LogInfo("签名key=val&...串 >" . $params_str);

        $params_sha1x16 = sha1($params_str, false);
//        $log->LogInfo("摘要sha1x16 >" . $params_sha1x16);

        $private_key = self::getPrivateKey($cert_path, $cert_pwd);
        // 签名
        $sign_falg = openssl_sign($params_sha1x16, $signature, $private_key, OPENSSL_ALGO_SHA1);
        if ($sign_falg) {
            $signature_base64 = base64_encode($signature);
//            $log->LogInfo("签名串为 >" . $signature_base64);
            return $signature_base64;
        } else {
//            $log->LogInfo(">>>>>签名失败<<<<<<<");
        }
//        $log->LogInfo('=====签名报文结束======');
    }

    public static function getSignCertId($cert_path, $cert_pwd)
    {
        $pkcs12certdata = file_get_contents($cert_path);
        openssl_pkcs12_read($pkcs12certdata, $certs, $cert_pwd);
        $x509data = $certs ['cert'];
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        $cert_id = $certdata ['serialNumber'];
        return $cert_id;
    }

    public static function getPulbicKeyByCertId($x509data, $cert_id)
    {
        $result = null;
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        if ($cert_id == $certdata['serialNumber']) {
            $result = openssl_get_publickey($x509data);
        }
        return $result;
    }

    public static function getEncryptKey($cert_path)
    {
        $x509data = file_get_contents($cert_path);
        return $x509data;
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        return $certdata['cert'];
    }
}
